Tutorial

MEAN App with Angular 2 and the Angular CLI

Draft updated on Invalid Date
Default avatar

By Chris Ganga

MEAN App with Angular 2 and the Angular CLI

This tutorial is out of date and no longer maintained.

Introduction

One of the most common stacks that accompany Angular as a web framework is the MEAN stack. MEAN simply stands for MongoDB, Express, Angular, and Node.js, and is a concept that came about a few years ago with the growing popularity of all those technologies, and the fact that you only needed to know one language, JavaScript, to get started.

To explain the different parts:

  • MongoDB usually acts as the database for your application, in case you need to persist data. It’s where we store records.
  • ExpressJS is a web framework for Node.js, usually used as a backend for web apps in the MEAN stack.
  • Angular is usually the client-side MVC web framework. In this case, we will be using Angular 2.*.
  • NodeJS powers express, and will be the layer our server runs on.

We’ve previously written about setting ap a mean single application page. Since then, we’ve seen Angular release a newer version, Angular 2, and with it the Angular CLI, that enables quick scaffolding of Angular 2 apps.

This article will focus on setting up a MEAN app with Angular 2, while still using the Angular CLI.

Prerequisites

We’ll of course need the Angular CLI

  1. npm install -g angular-cli

You also need to know a little about Creating Angular apps with the Angular CLI, as well as Creating an Express app.

Creating the Angular App

Next, we’ll create an angular app with the CLI. If you’d like to use yarn please check below after the command.

  1. ng new mean-app

Note: If you have yarn installed, simply skip npm then run yarn later. Otherwise, the above command should be fine.

  1. ng new mean-app --skip-npm
  2. cd mean-app
  3. yarn

Both of the above approaches will scaffold an Angular 2 app and install all the dependencies. To serve the app, simply run

  1. ng serve

And open http://localhost:4200 in your browser.

Adding Express

Angular CLI comes with a command ng build, which bundles your Angular app into a dist folder, or a folder that you may specify in the angular-cli.json file. This is what our Express app will point to.

Install express and body-parser as dependencies.

  1. npm install --save express body-parser

Or if using yarn

  1. yarn add express body-parser

Then create a file server.js and a folder server in the root of our angular project. The server.js file will have the server code, that will point to the server folder, where the rest of the server implementation is.

server.js
// Get dependencies
const express = require('express');
const path = require('path');
const http = require('http');
const bodyParser = require('body-parser');

// Get our API routes
const api = require('./server/routes/api');

const app = express();

// Parsers for POST data
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));

// Point static path to dist
app.use(express.static(path.join(__dirname, 'dist')));

// Set our api routes
app.use('/api', api);

// Catch all other routes and return the index file
app.get('*', (req, res) => {
  res.sendFile(path.join(__dirname, 'dist/index.html'));
});

/**
 * Get port from environment and store in Express.
 */
const port = process.env.PORT || '3000';
app.set('port', port);

/**
 * Create HTTP server.
 */
const server = http.createServer(app);

/**
 * Listen on provided port, on all network interfaces.
 */
server.listen(port, () => console.log(`API running on localhost:${port}`));

The above code sets up a simple express app, with an /api route and all other routes are directed towards the dist/index.html page. This catch-all route, denoted with *, MUST come last after all other API routes have been defined.

The /api route points to a file ./server/routes/api.js. Let’s create this file.

server/routes/api.js
const express = require('express');
const router = express.Router();

/* GET api listing. */
router.get('/', (req, res) => {
  res.send('api works');
});

module.exports = router;

One last thing before we run this. Since the catch-all route is pointing to dist/index.html, we need to do a build of the Angular app.

  1. ng build

This creates the dist folder with the Angular 2 app built files. Now we can serve the app with express.

  1. node server.js

Going to http://localhost:3000 should load the app, and http://localhost:3000/api should show as below.

angular app (http://localhost:3000)

express API (http://localhost:3000/api)

Server data

Now that we have the API set up. Let’s quickly mock up some data for three route endpoints. We’ll call the jsonplaceholder mock API to respond with some data.

In a real MEAN app, however, this data should be retrieved from Mongo DB. To read more on this, you can go through connecting a node app to MongoDB.

First add axios for making HTTP requests.

  1. npm install --save axios

Or, if using yarn

  1. yarn add axios

Then, update the api.js file to have the following content.

server/routes/api.js
const express = require('express');
const router = express.Router();

// declare axios for making http requests
const axios = require('axios');
const API = 'https://jsonplaceholder.typicode.com';

/* GET api listing. */
router.get('/', (req, res) => {
  res.send('api works');
});

// Get all posts
router.get('/posts', (req, res) => {
  // Get posts from the mock api
  // This should ideally be replaced with a service that connects to MongoDB
  axios.get(`${API}/posts`)
    .then(posts => {
      res.status(200).json(posts.data);
    })
    .catch(error => {
      res.status(500).send(error)
    });
});

module.exports = router;

If we now stop the server and run it again, node server.js, we should see JSON data when we go to http://localhost:3000/api/posts

I’m using a json-viewer chrome plugin. You may see a not so pretty JSON response.

Angular route, component, and provider

We’ll add an angular component, then add a route that displays this component’s template.

Add an Angular component with the Angular CLI

  1. ng generate component posts

This adds a new folder in the src/app directory, called posts. If you’ve done a little getting started with Angular 2, the generated content of the component should be clear. We’ll edit them when the time comes.

The above command also imports the generated PostComponent in the src/app/app.module.ts file and adds it to the declarations property of the @NgModule decorator.

src/app/app.module.ts
// Imports commented out for brevity

import { PostsComponent } from './posts/posts.component';

@NgModule({
  declarations: [
    AppComponent,
    PostsComponent // Posts Component injected here
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Next, we’ll add a posts route. There are a couple of recommended ways to add routes to your Angular 2 apps, and that is out of scope for this guide. We’ll use the simplest and most straightforward.

src/app/app.module.ts
// Imports commented out for brevity
import { RouterModule } from '@angular/router';

import { AppComponent } from './app.component';
import { PostsComponent } from './posts/posts.component';

// Define the routes
const ROUTES = [
  {
    path: '',
    redirectTo: 'posts',
    pathMatch: 'full'
  },
  {
    path: 'posts',
    component: PostsComponent
  }
];

@NgModule({
  declarations: [
    AppComponent,
    PostsComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    RouterModule.forRoot(ROUTES) // Add routes to the app
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

We are simply telling our router that whenever the root route / is visited, redirect to /posts. We then declare the /posts route.

One final thing to complete our routing is to first make sure that we have a <base href="/"> in the src/index.html head tag. And then add a router-outlet where the route should be rendered. We’ll add this in the src/app/app.component.html

src/app/app.component.html
<h1>
  {{title}}
</h1>
<router-outlet></router-outlet>

We need to do a build and serve the app, we could do

  1. ng build && node server.js

Or just create an npm script within the package.json.

package.json
{
  "name": "mean-app",
  // meta data
  "scripts": {
    // Other scripts
    "build": "ng build && node server.js"
  },
  "private": true,
  "dependencies": {
    ...
  },
  "devDependencies": {
    ...
  }
}

Then simply run.

  1. npm run build

Going to http://localhost:3000 should redirect you to http://locahost:3000/posts, based on the instructions we gave to our router.

.

Connecting Component to Express API

Angular 2’s best practices recommend defining a provider or service to handle the HTTP calls. So, we’ll generate one with the Angular CLI.

  1. ng generate service posts

This creates a posts.service.ts in the src/app directory. We then need to add it in the providers’ section of our module declaration.

src/app/app.module.ts
// Imports commented out for brevity

import { PostsService } from './posts.service';

// Routes

@NgModule({
  declarations: [
    AppComponent,
    PostsComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    RouterModule.forRoot(ROUTES)
  ],
  providers: [PostsService], // Add the posts service
  bootstrap: [AppComponent]
})
export class AppModule { }

Then make the HTTP call within the service to our Express server.

src/app/posts.service.ts
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import 'rxjs/add/operator/map';

@Injectable()
export class PostsService {

  constructor(private http: Http) { }

  // Get all posts from the API
  getAllPosts() {
    return this.http.get('/api/posts')
      .map(res => res.json());
  }
}

Then import our service in the Post component.

src/app/posts/posts.component.ts
import { Component, OnInit } from '@angular/core';
import { PostsService } from '../posts.service';

@Component({
  selector: 'app-posts',
  templateUrl: './posts.component.html',
  styleUrls: ['./posts.component.css']
})
export class PostsComponent implements OnInit {
  // instantiate posts to an empty array
  posts: any = [];

  constructor(private postsService: PostsService) { }

  ngOnInit() {
    // Retrieve posts from the API
    this.postsService.getAllPosts().subscribe(posts => {
      this.posts = posts;
    });
  }
}

And finally, we’ll just display the posts in the view.

src/app/posts/posts.component.html
<div class="container">
  <div class="row" *ngFor="let post of posts">
    <div class="card card-block">
      <h4 class="card-title">{{ post.title }}</h4>
      <p class="card-text">{{ post.body }}</p>
      <a href="#" class="card-link">Card link</a>
      <a href="#" class="card-link">Another link</a>
    </div>
  </div>
</div>

This is just a Bootstrap 4 card, looping through our posts and binding the title and the body properties. I also added the Bootstrap CDN to the src/index.html page in the head tag.

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.5/css/bootstrap.css">

Run the app.

  1. npm run build

Going to localhost:3000 should produce this.

Conclusion

There is some sort of server-client separation in the approach we took. Both the Angular app and the Express server can be built independently.

You can now just continuously use the Angular CLI to build out your Angular app while developing the Express server just as you would any Node.js Express App.

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about us


About the authors
Default avatar
Chris Ganga

author

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
Leave a comment


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Get our biweekly newsletter

Sign up for Infrastructure as a Newsletter.

Hollie's Hub for Good

Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

Become a contributor

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

Welcome to the developer cloud

DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

Learn more
DigitalOcean Cloud Control Panel