Select Page

The Website

Angular 2 Website Screenshot

Angular 2 has finally hit beta which was the mental milestone for me to start writing Angular 2 content. Hooray! To kick things off, I wanted to port a project that I covered in my Building a Website with AngularJS Pt 2 – Now With State! post to Angular 2. We are going to get acquainted with the brave new world of Angular 2 by building out a simple website so we can see how the Angular 2 pieces come together.

Grab the code from the repository below and let’s go!

Code

Compile and Serve

There are two main ways to compile and serve an Angular 2 application and that is via webpack or systemjs. I have personally fallen in love with webpack, and that is what we will use to build this app. I am not going to go too far into the details of the build system as there are a myriad of quality Angular 2 Webpack seed projects that will jumpstart your Angular 2 app. See the Resources section for more details.

You can write your Angular 2 application in ES5, EcmaScript 2015 or TypeScript, but the framework lends itself best to TypeScript. Writing in TypeScript requires a bit more setup but the return on investment is tenfold in terms of productivity and clarity in our code.

When you download the repository, the first thing you need to do is run npm i to install the package dependencies.

tsconfig.json

One of the package dependencies that we are going to install is typescript, which we will use to compile our TypeScript into the Type O negative version of JavaScript: the universal donor, ES5. We need to tell our compiler how we want to compile our TypeScript files, so we need to create a tsconfig.json file. The file below reads pretty well, but I will call out the two most important properties and those are “target”: “ES5” and “module”: “commonjs”. We are setting our ECMAScript version to ES5 and indicating that we want to generate our modules in the commonjs format.

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "sourceMap": true,
    "suppressImplicitAnyIndexErrors":true
  },
  "compileOnSave": false,
  "buildOnSave": false,
  "exclude": [
    "node_modules"
  ],
  "filesGlob": [
    "app/**/*.ts",
    "typings/browser.d.ts"
  ],
  "atom": {
    "rewriteTsconfig": false
  }
}

packages.json

We have defined how we want our TypeScript to be compiled, now we need to create a hook for the work to be done. In our packages.json file, we have defined a task that uses webpack-dev-server along with webpack to bundle our TypeScript modules and other assets into a single javascript file and then serve the app.

"scripts": {
  "start": "webpack-dev-server --inline --colors --watch --display-error-details --display-cached  --port 3001 --hot"
}

We then call npm start when we are ready to compile and serve our application.

Bootstrapping

The first mystery of Angular 2 for me was “How in the world do I even run the app!?”. The second mystery was “Okay, so how do I bootstrap the application?!”.

The first thing we need to do to bootstrap our application is to include the necessary resources into our index.html file. Because webpack is bundling everything into a single file, we only need to include one bundle.js file.

<script src="bundle.js"></script>

Inside the boot.ts file, we are importing three components; bootstrap, ROUTER_PROVIDERS and AppComponent. We are also importing core-js which is an ES6 polyfill, as well as zone.js which Angular uses for change detection. We then instantiate our application and specify our root level component, AppComponent and inject ROUTER_PROVIDERS as a submodule.

import 'core-js';
import 'zone.js/dist/zone';

import {bootstrap} from '@angular/platform-browser-dynamic';
import {ROUTER_PROVIDERS} from '@angular/router';
import {AppComponent} from './app.component';

bootstrap(AppComponent, [
  ROUTER_PROVIDERS
]);

Back in our index.html file, our entry point in the markup happens at this line: <app>Loading…</app>. Angular has instantiated AppComponent and loaded its template into the app element.

<body>
    <app>Loading...</app>
</body>

We have just covered how to compile, serve and bootstrap an Angular 2 application. Let us unpack what a component consists of so that we can make the connection between how AppComponent becomes app on our page.

The App Component

Angular components are really just JavaScript classes wrapped with love in Angular 2 metadata.

If you haven’t read my CIDER: My Checklist for Creating Angular 2 Components post, check it out. You will see the CIDER process in effect here.

The first thing I do when creating a component is to create the class. I will usually just stub it out because I will enhance it later.

export class AppComponent {}

The next thing we are going to do is to import our dependencies. In this case, we just need to import Component from @angular/core.

import {Component} from '@angular/core';

And from here, we are going to decorate our class by adding @Component metadata to tell our application how we want the AppComponent to behave. We are defining the HTML element we want this class to target in the selector property as well as setting our template and styles via templateUrl and styleUrls, respectively. We are also injecting ROUTER_DIRECTIVES into our component at the directives property.

@Component({
  selector: 'app',
  templateUrl: require('app/app.component.html'),
  styleUrls: [require('app/app.component.css')],
  directives: [ROUTER_DIRECTIVES]
})
export class AppComponent {}

This is where things get a bit fluid in the CIDER process. I will often spend n iterations enhancing my components and this could go on indefinitely. In our case, we want to add in routing to our application and so we are going to import the appropriate modules and decorate our component with @Routes. To enable routing, we are going to import Routes and ROUTER_DIRECTIVES as well as AboutComponent, ExperimentsComponent and HomeComponent.

import {Routes, ROUTER_DIRECTIVES} from '@angular/router';
import {AboutComponent} from './about/about.component';
import {ExperimentsComponent} from './experiments/experiments.component';
import {HomeComponent} from './home/home.component';

We need the routing modules to enable routing and the additional components for our routing table. We are passing in an array of route definitions into @Routes, which tell us the path of the route and what component is going to be mapped to that route. We want our home route to be our default route; to accomplish this, we do two things. First, we add a ‘/’ route to handle navigations to the top-level domain and reroute them to the home component. Then we will add in a ‘/*’ route to catch all other routes and reroute them to the home component. We will circle back around and talk about how the templates deal with routes in a moment but the basic gist of the new component router in Angular 2 is that each route maps to a component. Not really sure why they called it ComponentRouter though… JK!

@Routes([
  {path: '/',            component: HomeComponent },
  {path: '/home',        component: HomeComponent},
  {path: '/about',       component: AboutComponent },
  {path: '/experiments', component: ExperimentsComponent },
  {path: '/*',           component: HomeComponent }
])

We are also going to import StateService and ExperimentsService into our component and then decorate our component on the providers property which we will cover later. And I now present the #drumRoll AppComponent in its entirety!

import {Component} from '@angular/core';
import {Routes, ROUTER_DIRECTIVES} from '@angular/router';
import {AboutComponent} from './about/about.component';
import {ExperimentsComponent} from './experiments/experiments.component';
import {HomeComponent} from './home/home.component';
import {StateService} from './common/state.service';
import {ExperimentsService} from './common/experiments.service';

@Component({
  selector: 'app',
  templateUrl: require('app/app.component.html'),
  styleUrls: [require('app/app.component.css')],
  directives: [ROUTER_DIRECTIVES],
  providers: [StateService, ExperimentsService],
})
@Routes([
  {path: '/',            component: HomeComponent },
  {path: '/home',        component: HomeComponent},
  {path: '/about',       component: AboutComponent },
  {path: '/experiments', component: ExperimentsComponent },
  {path: '/*',           component: HomeComponent }
])
export class AppComponent {}

I really dig how everything up to this point has been self-documenting in its intentions and purpose. There isn’t a single piece in this component that I cannot immediately figure out what its purpose is.

To complete the CIDER process, we are going to repeat the process on a sub-component. All three of our components defined in our router are pretty much the same and so we are going to focus on the HomeComponent. There is some additional functionality in the ExperimentComponent that we will cover in a later post.

The Home Component

We are going to break ground on our HomeComponent by defining the HomeComponent class. We are also defining and initializing two properties on our class for the component’s title and body. This is a website after all!

export class HomeComponent {
  title: string = 'Home Page';
  body:  string = 'This is the about home body';
}

We will import the appropriate dependencies.

import {Component} from '@angular/core';

We will then decorate our class and set the selector and templateUrl properties.

@Component({
    selector: 'home',
    templateUrl: require('app/home/home.component.html')
})

We are going to use the StateService to store state between our routes and so we will add that to our component. Dependency injection within Angular 2 happens at the constructor and so we are going to add one to our class and inject the StateService. Components also have lifecycle hooks that we can use to sequence units of work. In our case, we want to retrieve and set our message from StateService when our component is initialized. We will use the ngOnInit hook to make that call for us. We don’t have to import OnInit and extend the class, but it is a good idea semantically.

import {Component, OnInit} from '@angular/core';
import {StateService} from '../common/state.service';

export class HomeComponent implements OnInit {
  title: string = 'Home Page';
  body:  string = 'This is the about home body';
  message: string;

constructor(private stateService: StateService) { }

ngOnInit() {
    this.message = this.stateService.getMessage();
  }

updateMessage(m: string): void {
    this.stateService.setMessage(m);
  }
}

Again, incredibly self-documenting and easy to read.

The State Service

We are going to create the StateService class and then expose it as a provider to our application. I am not going to offer too much commentary on the code below because it is so rudimentary in nature. It is essentially a service that has a getter and a setter for a message property.

export class StateService {
  private message = 'Hello Message';

getMessage(): string {
    return this.message;
  };

setMessage(newMessage: string): void {
    this.message = newMessage;
  };
}

Things get interesting when we want to make StateService available for injection into other components. The first step is to import Injectable into our class.

import {Injectable} from '@angular/core';

And then we decorate it with @Injectable().

@Injectable()
export class StateService {
  private message = 'Hello Message';

getMessage(): string {
    return this.message;
  };

setMessage(newMessage: string): void {
    this.message = newMessage;
  };
}

We have spent most of our time working through building our components, so let us complete the loop by locking down our templates.

The Views

Using home.component.html as our reference point, let us take an abbreviated tour of the updated template syntax in Angular 2.

One-way data binding is defined exactly like it was in Angular 1.x through string interpolation.


<h1>{{title}}</h1>
{{body}}

User input events are no longer captured by adding custom Angular directives to our markup but rather through capturing native DOM events and wrapping them in parenthesis. We can see this in the code below as we capture the click event with (click)=”updateMessage(message)” and call updateMessage.

<button type="submit" class="btn" (click)="updateMessage(message)">Update Message</button>

Two-way data binding in Angular 2 is basically one-way data binding applied twice. We talked about binding using string interpolation but we can also bind to properties using brackets syntax. We combine property binding (component to view) and event binding (view to component) to accomplish two-way data binding. The solution is surprisingly simple as we wrap our ngModel in both brackets and parenthesis to make [(ngModel)]=”message”.

<input type="text" [(ngModel)]="message" placeholder="Message">

For context, here is the entire home template.


<h1>{{title}}</h1>
{{body}}

<hr>
<div>
<h2 class="text-error">Home: {{message}}</h2>
<form class="form-inline">
      <input type="text" [(ngModel)]="message" placeholder="Message">
      <button type="submit" class="btn" (click)="updateMessage(message)">Update Message</button>
    </form>
</div>

Routing Markup

We are back to where we started as we wrap up this lesson with a discussion on the template syntax for routing in app.component.html. When we define a route, where does the component’s template actually get inserted? We set that insert point with router-outlet. It is the new ngView.


<div id="container">
    <router-outlet></router-outlet>
</div>

Great! So how do we navigate from one route to another? We do that with routerLink in the form of [routerLink]=”[‘/home’]” as we can see in the code below.


<h1 id="logo">
  <a [routerLink]="['/home']"></a>
</h1>
<div id="menu">
  <a [routerLink]="['/home']" class="btn">Home</a>
  <a [routerLink]="['/about']" class="btn">About</a>
  <a [routerLink]="['/experiments']" class="btn">Experiments</a>
</div>

And the entire app.component.html.


<header id="header">
<h1 id="logo">
    <a [routerLink]="['/home']"></a>
  </h1>
<div id="menu">
    <a [routerLink]="['/home']" class="btn">Home</a>
    <a [routerLink]="['/about']" class="btn">About</a>
    <a [routerLink]="['/experiments']" class="btn">Experiments</a>
  </div>
<div class="color"></div>
<div class="clear"></div>
</header>
<div class="shadow"></div>
<div id="container">
  <router-outlet></router-outlet>
</div>

Review

Before you raise your billable rates by 15% and start selling this sweet Angular 2 website, let us do a quick review of what we covered.

  • We define how we want to compile TypeScript in our tsconfig.json file.
  • We use webpack-dev-server to compile and serve our application.
  • We saw how to use CIDER to build out our AppComponent and HomeComponent
  • We learned how to created an injectable service using @Injectable() metadata.
  • We did an abbreviated tour of the Angular 2 binding syntax.
  • We learned how routes are added to our view with router-outlet and the syntax for navigating to a route using routerLink

Let me know what you else you would like to learn in the comments below and in the meantime… #highFive!

Resources

Angular 2 Template Syntax

tsconfig.json Documentation

TypeScript Documentation

Webpack

Webpack Dev Server

Code