Scaffold an Express Front End with Angular CLI

In my last post I explained how to build scalable Web API’s with Express and Async/Await.  The next chapter in the story is to build a cross-platform front end that consumes the back end Web API.  While there are a number of cross-platform UI technologies to choose from, such as Xamarin (C#), Swing (Java) or Kivy (Python), Angular (JavaScript) has garnered attention because it crosses the chasm that has separated web, desktop and mobile platforms, and it’s designed to be used with TypeScript, which increases developer productivity by adding static types and incorporating features from current and future versions of JavaScript.

Clone, fork or download the code for this post: https://github.com/tonysneed/Demo.Express.Angular-CLI

angular2-logo

Starting from Scratch

If you’re getting up to speed with Angular, you’ll want to check out the quickstart guide and source code, which describe how to build an Angular application from scratch.  To set up an Angular Hello World app, you’ll need to perform the following steps:

  1. Initialize package.json
  2. Install Angular packages
  3. Set TypeScript options with tsconfig.json
  4. Configure SystemJS with systemjs.config.js
  5. Add index.html host web page
  6. Create root app module
  7. Create root app component
  8. Create root app template
  9. Add main.ts platform bootstrapper

Whew! That’s a lot to go through to get started with a basic Angular application — and you haven’t even added your first component!

Introducing Angular CLI

To keep you from having to perform all these steps manually, Angular has created a tool called Angular CLI (in beta at the time of this writing), which you can use to scaffold new Angular projects, as well as add components to existing projects.

angular-cli

After installing Node.js, install angular-cli globally using NPM (Node Package Manager). While the documentation points you in the direction of creating a new app with Angular CLI, what I’d like to illustrate in this blog post is using Angular CLI to add a web front end to an existing project that includes a Web API built using Node with Express.

Begin with Express

For this purpose, I have created a starter branch in the code repository for this post. If you clone the repo, you can easily switch to the starter branch using GitHub Desktop.

ng-demo-starter.png

Begin by launching Visual Studio Code by opening a terminal at the repo location and entering  code. (you can use another code editor if you prefer).  Press Cmd + ` to open the integrated terminal in VS Code and enter npm install to restore package dependencies.  In the README.md file you’ll find instructions for building and running the Web API, namely, press Cmd + P and enter task serve-api.

Open a browser to localhost:/3000/api/products, and you should see JSON for several products.  Then terminate the running task by pressing Cmd + Shift + P, entering Terminate and selecting the first item.  (You may want to create a keyboard shortcut to terminate running tasks.)

products-json.png

Get Productive

Now that you have a functioning Web API using Express, it’s time to add a front end using Angular-CLI.  Open a terminal at the project root and enter ng init.  You’ll be prompted to overwrite a few files.  Enter Y to overwrite each of the files.  Don’t worry, you’ll use VS Code’s Git integration to see file differences and restore the original contents.

ng-init-overwrite-png.png

Back in VS Code, click on the Git icon to view file changes.  To restore the original content to overwritten files, simply select the changed file and copy content from the left to the right side.  You don’t need to change tslint.json, and the only change to .gitignore will be to add the dist folder.  But you’ll need to copy dependencies from the original to the new package.json file.

ng-init-package-diff.png

After updating both dev and runtime dependencies, run npm install to add packages required by Angular CLI.  Then go back to the terminal and enter npm start, which in turn will run ng serve to start a web server with browser live reload.  Open a browser to localhost:4200/, and you’ll see the root app component hosted in index.html.

ng-new-browser.png

To prove that live reload is working, you can open app.component.ts in VS Code and change the title property to 'Hello Angular CLI!'.  A few seconds later, you’ll see the change reflected in the browser without you having to refresh the page.  To stop the web server, go back to the terminal and press Ctrl + C.

Add Some Style

While Angular CLI created a functioning Angular app in very little time, the look is rather plain.  To spice things up, you’ll want to add some styling with a framework such as Angular Material or Bootstrap.  Because we’re building a straightforward app for this demo, Bootstrap will do fine.

For instructions on adding Bootstap with an app generated by Angular CLI, see the section called Global Library Installation in the ReadMe for the Angular CLI project on GitHub, as well as the ReadMe for ng2-boostrap.  Start by running:

npm install ng2-bootstrap bootstrap jquery --save

Open angular-cli.json and insert a new entry into the styles array.

"styles": [
        "styles.css",
        "../node_modules/bootstrap/dist/css/bootstrap.min.css"
],

Next insert two entries into the scripts array.

"scripts": [
  "../node_modules/jquery/dist/jquery.js",
  "../node_modules/bootstrap/dist/js/bootstrap.js"
],

Now you can apply Boostrap styles to HTML in your application.  Start by placing a <div> around the <h1> tag in app.component.html and setting class to "container".

<div class="container">
  <h1>
    {{title}}
  </h1>
</div>

This will apply the container bootstrap style to the root app component.  You may also wish to apply a different style to elements in the app component. You can accomplish this by setting styles in app.component.css.

h1 {
  color: #369;
  font-family: Arial, Helvetica, sans-serif;
  font-size: 250%;
}

With these changes in place, start the app by running npm start and browse to the home page. You should see a more attractive user interface.

add-bootstrap.png

Your First Component

Now it’s time to scaffold your first component — how exciting!  Once again, Angular CLI will lend a helping hand.  To find out what it can do for you, have a look at the Angular CLI Reference document, which lists top-level commands.  Or you can enter ng --help at the command line, and then drill into a command by appending the name of the command you want to explore.  For example, to find out about the generate command enter: ng --help generate.  From the output you’ll see items you can generate (called “blueprints”).

Let’s start by generating a product class.  Since you may wish to share it among various components, you create  shared/models directories beneath app, then run:

ng generate class shared/models/product

This will generate a Product class in the shared/models folder, but it doesn’t have any properties. You can simply add a constructor with public parameters for each property you’d like to add.

export class Product {
  constructor(
    public productId: number,
    public productName: string,
    public unitPrice: number) { }
}

Now that you have a model, the next step is to generate a component, which you can produce by running ng generate with the component blueprint.  If you’re tired of typing generate every time, you can substitute it with the alias g.

ng g component products

This will create a ProductsComponent class along with an HTML template, a CSS file and a spec file for testing the component, and place them all in a products folder under app.

Angular is a framework for building SPA’s (Single Page Applications) and implements an MVVM (Model-View-ViewModel) architecture, with components playing the role of the ViewModel and templates serving as the View.  The way you associate components and templates is by applying an @Component decorator to a component and include a templateUrl.

Open products.component.ts and import Product, then add a title property set to ‘Products’ and a products property that is an array of Product.  Later you’re going to set products by retrieving products from the Express Web API in your project, but for now you can simply initialize products in the ngOnInit function to some test products.  You should also include an error property in the unlikely event something goes wrong. 🙂

import { Component, OnInit } from '@angular/core';
import { Product } from '../shared/models/product';

@Component({
  selector: 'app-products',
  templateUrl: './products.component.html',
  styleUrls: ['./products.component.css']
})
export class ProductsComponent implements OnInit {

  title: string = 'Products';
  products: Product[];
  error: any;

  constructor() { }

  ngOnInit() {
    this.products = [
            new Product(1, 'Product 1', 10),
            new Product(2, 'Product 2', 20),
        ];
  }
}

Now that you have a component for products, you can create a template by opening products.component.html and replacing generated content with HTML that contains bindings to properties in the component. Angular uses double curly braces for what is called interpolation to evaluate an expression that can refer to component properties.  You can also use built-in directives, such as *ngIf and *ngFor to perform data binding in the template.  For the products template, we’ll insert an HTML table with bootstrap styling that lists the properties of each product. (Note in the following HTML snippet, you’ll need to substitute left and right brackets and ampersands within the pre tags.)

<div class='panel panel-primary'>
    <div class='panel-heading'>
        {{title}}
    </div>
    <div class='panel-body'>
        <div class='table-responsive'>
            <pre>&lt;table class='table' *ngIf="products &amp;&amp; products.length"&gt;</pre>
                <thead>
                    <tr>
                        <th>Product Name</th>
                        <th>Unit Price</th>
                    </tr>
                </thead>
                <tbody>
                    <pre>&lt;tr *ngFor="let product of products"&gt;</pre>
                        <td>{{ product.productId }}</td>
                        <td>{{ product.productName }}</td>
                        <td>{{ product.unitPrice }}</td>
                    </tr>
                </tbody>
            </table>
        </div>
        <pre>&lt;div class="alert alert-danger" *ngIf="error"&gt;</pre>
            <strong>Error!</strong> {{error}}
        </div>
    </div>
</div>

Lastly, you’ll need to edit app.component.html to include the 'app-products' selector from products.component.ts.

<div class="container">
  <h1>
    {{title}}
  </h1>
  <pre>&lt;app-products&gt;&lt;/app-products&gt;</pre>
</div>

Run npm start from a command prompt and navigate to http://localhost:4200/. You should see the products component displayed in the browser.

products-browser.png

Connect to the Web API

While we have a products component that works, it does not yet display products retrieved from the back end Express Web API.  To accomplish this, we’ll use Angular CLI to generate a products service.

ng g service shared/services/products

Add a private parameter to the constructor to inject the Angular Http client into the products service, then add a getProducts function that calls this._http.get and converts it to a promise, so that you can handle success by calling .then, or failure by calling .catch. Here is the code for the ProductsService class.

import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import 'rxjs/add/operator/toPromise';

import { Product } from '../models/product';
import { Urls } from '../constants';

@Injectable()
export class ProductsService {

  private _productsUrl = Urls.BaseUrl + 'api/products';

  constructor(private _http: Http) { }

  getProducts(): Promise {
    return this._http.get(this._productsUrl)
      .toPromise()
      .then(resp => resp.json() as Product[])
      .catch(this.handleError);
  }

  private handleError(error: any): Promise {
    console.error('An error occurred', error);
    return Promise.reject(error.message || error);
  }
}

In order to use ProductsService, you’ll need to add it as a provider to @NgModule in app.module.ts, after importing the ProductsService module.  Lastly, you’ll need to refactor ProductsComponent to use ProductsService, by adding a private parameter to the constructor, then calling getProducts to set the products property if successful or the error property if unsuccessful.

import { Component, OnInit } from '@angular/core';
import { Product } from '../shared/models/product';
import { ProductsService } from '../shared/services/products.service';

@Component({
  selector: 'app-products',
  templateUrl: './products.component.html',
  styleUrls: ['./products.component.css']
})
export class ProductsComponent implements OnInit {

  title: string = 'Products';
  products: Product[];
  error: any;

  constructor(private _productService: ProductsService) { }

  ngOnInit() {
    // Replaced with products service
    // this.products = [
    //         new Product(1, 'Product 1', 10),
    //         new Product(2, 'Product 2', 20),
    //     ];

    this._productService.getProducts()
      .then(products => this.products = products)
      .catch(error => this.error = error);
  }
}

See It in Action

It’s finally time to run the front and back ends of your Angular-Express application at the same time. But for it to succeed, you’ll need to add a proxy to the back end so that calls to the Web API on port 4200 will be sent to port 3000 instead.  Add a file to the project root called proxy.conf.json with the following content:

{
  "/api": {
    "target": "http://localhost:3000",
    "secure": false
  }
}

Then update the start script in package.json to append a proxy-config parameter.

"start": "ng serve --proxy-config proxy.conf.json"

With the back end proxy configured, you can start the Express Web API by running the serve-api gulp task.  Then you can start the Angular app by running npm start.  You should now see a list of products that have been retrieved from the Web API.

products-final-browser.png

Clone, fork or download the code for this post: https://github.com/tonysneed/Demo.Express.Angular-CLI

Congratulations!  You have used Angular CLI to scaffold a front end to an Express Web API back end.  Because the front end is independent of the back end, there’s nothing to stop you from swapping out the Express implementation for another technology stack, such as ASP.NET Core, without affecting the Angular front end.

About Tony Sneed

Married with three children.
This entry was posted in Technical and tagged , , . Bookmark the permalink.

7 Responses to Scaffold an Express Front End with Angular CLI

  1. bhroberts52 says:

    First you were my WCF guru, then you became my MVVM guru. Now you are my Web Api and Angular 2.0 guru.

    Thanks, Tony.

    Burton Roberts
    Extended Day Services

  2. Harry says:

    There is an error at the file app.module.ts. change line
    import { ProductsService } from ‘./shared/products.service’;
    to
    import { ProductsService } from ‘./shared/services/products.service’;

    By the way this needs two web servers to run. Is there an easy way to serve the angular app from within express js?

    • Tony Sneed says:

      There is an error at the file app.module.ts.

      Thanks for catching the error, which I’ve fixed in commit bb29c6b.

      This needs two web servers to run. Is there an easy way to serve the angular app from within express js?

      Good question. I intentionally set it up this way to maintain a cleaner separation of concerns between the Express and Angular pieces. This makes it easier to swap out Express for another back end, such as ASP.NET Core, without affecting the front end. Also, having two web servers allows you to make changes to either the front or back end without recompiling and restarting the other side. For example, you can keep the Express service running while making a change to the Angular app, and vice versa.

      Starting both servers in VS Code is easy. Just press Cmd + P, type task serve-api. Once the Express service starts, press Cmd + ` to open the integrated terminal in VS Code, and enter npm start.

      Have said that, it’s not hard to set up the Express part to support static files, so that you can use nodemon for both the front and back ends at the same time. See this gist for an example. The disadvantage is that you lose the live browser reload that ng-serve offers.

  3. obeliksz says:

    The code for the ProductService doesn’t work: Generic type ‘Promise’ requires 1 type argument(s).

  4. Anon says:

    Not sure if this can be classified as “scaffolding” as the CLI is merely generating starter files and codes and you have to write the rest (classes, objects, properties, .. etc)

    I miss dynamic data scaffolding from .NET where you just point to a database with tables and EF does its magic and you get a ready to use CRUD web application within minutes

    • Tony Sneed says:

      I hear you. Mostly you will hit a RESTful Web API from the front end. I don’t know if it exists, but it would be nice to have a way to integrate Angular-CLI with Swagger.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s