Building a todo list application with Angular 2.0

UPDATE: This blog post has been updated for Angular 2.0 beta (16th Dec 2015)

This blog post takes a step-by-step approach to building a simple todo-list application with Angular 2.0. Along the way we’ll look at web components, dependency injection, TypeScript, bindings and the Angular 2.0 change detection strategy which combine to make a much more elegant framework to its predecessor.

Introduction

I’ve used Angular 1.x on and off for the past couple of years, and must admit I’m not a great fan of the framework! It certainly gets the job done. It is functional and reliable, but to my eyes it isn’t terribly elegant.

Angular 2.0 is a radical departure for Angular 1.x, it is a complete re-write, with the only commonality between the two being the team that built them both. You can read about the motivations behind Angular 2.0 on Rob Eisenberg’s blog, but to summarise, the reasons are performance, new web technologies, mobile and simplification.

Angular 2.0 has been development for quite a while and as of March this year it became public on GitHub with an accompanying website. It is currently in alpha (alpha #46 to be precise), however I wouldn’t expect to see an initial release until early / mid 2016. Having said that, much of the infrastructure is in place and the alpha release gives us a very good indication of what Angular 2.0 will be.

In this blog post I want to present a step-by-step implementation of a simple Todo application, which is based on the well known TodoMVC project. The code for this post is all available via GitHub with a commit for each step of the implementation. If you want to follow along, you can checkout at each commit to see the code at the various stages.

Starter Project

As a starting point for the project I used an Angular 2 seed project, which puts in place a simple gulp build that performs the required TypeScript compilation and has live-reloading. I added the basic skeleton of the todo application to this seed, which can be seen in this commit. Running gulp serve shows the following:

Angular 2.0 works with JavaScript, Dart and TypeScript, however as it is written in TypeScript, my feeling is that this will be the de-factor language for Angular 2.0 apps.

Taking a quick look at the various files in this project, the app/component/todolist folder contains the only component within the starter project, which is defined in todolist.ts:

import {Component} from 'angular2/core';

@Component({
  selector: 'todo-list',
  templateUrl: 'app/todolist/todolist.html',
  styleUrls: ['app/todolist/todolist.css']
})
export default class TodoList {
}

The above code imports the Component decorator, a new feature in TypeScript 1.5 which is based on an ES7 proposal (which ultimately means it might become part of the JavaScript language). Decorators allow you to annotate classes with meta-data, in this case Angular is using the decorator to indicate that the TodoList class is an Angular component, with associated styles and templates.

The todolist.html and todolist.css files are plain-old HTML and CSS (Note, you can of course use SaSS or Less, but that would require a further build step).

An instance of this component is constructed within index.html, together with the steps required to load the various JavaScript files.

<body>
  <todo-list>Loading...</todo-list>
</body>

The project makes use of the SystemJS module loader, which is built on top of an ES6 module loader. Because browsers do not support ES6, the TypeScript compiler is configured to output JavaScript based on the ES5 specification, with a compatible module format. The net result is that the browser can resolve dependencies and load modules dynamically, although bundling is possible via the SystemJS Builder if you want to reduce the number of network requests.

The final piece required to make this application work is the bootstrapping, which is within boostrap.js:

import {bootstrap} from 'angular2/platform/browser';
import ToDoList from './todolist/todolist';

bootstrap(ToDoList);

This simply informs Angular that this component is the application root.

If you’ve come from Angular 1.x you’ll notice that there isn’t a single controller in sight, that’s because Angular 2.0 doesn’t use them - it’s all about the components …

Web components

With all this talk of components, you might expect that the your TodoList component is being rendered to the DOM as a web component. However that is not the case, if you inspect the DOM you will see something like this:

The component and its contents are being inserted as regular DOM elements with some odd looking attributes such as _ngcontent-aej-1. If you look at the CSS being served to the browser you can see the reason why … the selectors are being modified to include these attributes, e.g. .todoapp becomes .todoapp[_ngcontent-aej-1], in order to scope the CSS. Nasty!

Fortunately you can opt into using real web components by adding an encapsulation mode to the Component decorator:

import {Component, ViewEncapsulation} from 'angular2/core';

@Component({
  ...
  encapsulation: ViewEncapsulation.Native
})
export default class ToDoList {
}

This results in the component template being rendered to a shadow DOM:

The style is now scoped, removing the need for the generated attributes. You can see this in action via this commit.

Simple binding

One of the key features of any MVC framework is a binding mechanism, which simplify the process of keeping the DOM state in synch with an underlying model.

Angular 2.0 bindings are quite simple, just add a property to your class:

export default class TodoList {
  newItem = 'test';
}

And bind it to a DOM element’s property as follows:

<input class="new-todo"
  [value]="newItem">

Which gives the following:

(See this commit)

This is quite different from Angular 1.x, there is no concept of a scope, instead the component itself is used as the context for the template expression (i.e. the "newItem" part).

There are actually some more significant differences between the way Angular 1.x and Angular 2.0 evaluate bindings. With Angular 1 the framework binds to DOM element attributes, whereas with Angular 2 it binds to an element’s properties. This has numerous benefits, for more information I’d recommend watching the ng-conf 2015 keynote, I don’t want to get bogged-down in the details here, I want to keep this blog post focussed on the practical.

Event binding

Binding events is just as simple as binding properties, by adding a function to the component:

export default class TodoList {
  newItem = 'test';

  addClicked() {
    console.log('Add clicked', this.newItem);
  }
}

This can be bound as follows:

<button class="add" (click)="addClicked()">Add</button>

Now as you click the button the logging code in the component is executed.

Notice the subtle difference, in the earlier example you saw square braces for property binding, [] and round for event binding () - simple!

Two-way binding

There’s a problem with the current implementation, you can edit the todo item text, however the underlying newItem property is not updated. Currently the application has a one-way binding, from the component to the view. This needs to be swapped for a two-way binding, where changes in the view are propagated back to the component. However, before adding a two-way binding, let’s see how this might be achieved manually.

The HTML input element exposes an input event which is fired when the value changes (it’s similar to keyup, but catches a few more cases):

<input class="new-todo"
      [value]="newItem" (input)="newItemChanged($event)">

The component can be updated to add this new event handler:

newItemChanged(event: KeyboardEvent): void {
  const target = <HTMLInputElement> event.target;
  this.newItem = target.value;
}

The $event variable is available as part of the template expression context and contains the object that describes the DOM event. The variable contains a reference to the element which fired the event, allowing access to the updated value. As a result, when the ‘Add’ button is clicked it now reports the correct text.

Checkout this commit to see it in action.

It works, but it feels a bit messy.

Local template variables

Rather than access the DOM element indirectly via an event, it is possible to obtain a more direct reference to it via a template variable. Updating the template as follows:

<input class="new-todo" placeholder="What needs to be done?" autofocus
      #todoInput [value]="newItem" (input)="newItemChanged(todoInput.value)">

The #todo-input creates a local template variable that is accessible on any child or sibling element. It is then referenced in the input event binding as todoInput. Notice that this is a de-normalised name, which is required because HTML is case insensitive, a concept you might have come across from writing Angular 1 directives.

With this code in place the component logic is simplified:

newItemChanged(value: string): void {
  this.newItem = value;
}

With the application still working as expected. Try it out via this commit.

This still feels like quite a cumbersome mechanism for two-way binding. Fortunately there is a further simplification …

ngModel binding

Angular 2 has a number of features that support the building of forms, including form submission, error handling, etc … One of these features is the ngModel directive that supports two way binding.

You can add two-way binding to a DOM element via ngModel as follows:

<input class="new-todo"
      [(ngModel)]="newItem">

You can see this in action via the following commit.

Notice the funny syntax [(ngModel)] which is a combination of property and event binding. This hints at the fact that this directive is basically a wrapper around a property binding [ngModel] and accompanying event binding (ngModelChange) (this event is exposed by the directive, rather than the DOM element).

Pseudo events

It would also be good if the application added todo items when the user hits enter. For this you can add a binding to the keyup event and filter based on the key that was pressed. Although Angular 2 simplifies this by providing pseudo-events which have a filter built in.

The following snippet shows how the addItem component method can be invoked when either the button is clicked, or the enter key pressed, via the keyup.enter pseudo event:

<input class="new-todo"
  [(ngModel)]="newItem" (keyup.enter)="addItem()">
<button class="add" (click)="addItem()">Add</button>

See it in action via this commit.

Adding a store

The next step is to render a list of todo items, which would ultimately be persisted. Currently the application doesn’t have a suitable place to add long-lived state or persistence logic. With Angular 1.x you’d have probably created a service for this purpose, however Angular 2 doesn’t have services.

Rather than adding a service, I’m going to borrow the store concept from the Flux pattern. Stores contain application state and perform logic based on actions that are dispatched from the view. I’ll not be adding actions or a dispatcher to this application, but will keep the logic and state within the store.

Here’s a very simple store with a hard-coded list of todo items:

export class TodoItem {
  text: String;
}

export class TodoStore {
  items: TodoItem[] = [
     {text: 'Feed the cat'},
     {text: 'Do the shopping'}
  ];
}

You can construct an instance of this store within the todo-list component, and expose it via a property:

import {TodoStore} from './../store/todoStore';

const store = new TodoStore();

@Component({ ... })
export default class TodoList {
  newItem = 'test';
  items = store.items;
}

These can then be rendered via the ngFor directive as follows:

<section class="main">
  <ul class="todo-list">
    <li *ngFor="#item of items">
      <div class="view">
        <label></label>
      </div>
    </li>
  </ul>
</section>

Which renders as follows:

(See this commit)

That *ngFor binding is an odd looking one! It is one of a small class of directives called structural directives, which add / remove element sub-trees. In a similar fashion to the [(ngModel)] binding, *ngFor actually expands to the following:

<template ngFor #item [ngForOf]="items">
  <li>
    <div class="view">
      <label></label>
    </div>
  </li>
</template>

(See this commit)

The concepts above should actually look a bit more familiar. The template element is part of the web components specification, so is not specific to Angular 2.0. The #item of items part expands to #item [ngForOf]="items", which is just a regular property binding and a local template variable.

Personally I am not sure *ngFor="#item of items" makes things much clearer than the expanded form, which uses familiar concepts.

Dependency Injection

Constructing an instance of the store directly within the component isn’t ideal, it is likely that we would want the store to be a singleton. This was one of the important features of Angular 1.x services. Angular 2.0 has a solution to this problem in the form of dependency injection.

Rather than constructing an instance of the store, the component can be changed to require a store via its constructor:

import {TodoStore} from './../store/todoStore';

@Component({ ... })
export default class ToDoList {
  newItem = 'test';
  store: TodoStore;

  constructor(store: TodoStore) {
    this.store = store;
  }
}

You can then let Angular take care of injecting a store instance. All you have to do is provide a the TodoStore class to the bootstrap:

import {bootstrap} from 'angular2/platform/browser';
import ToDoList from './component/todolist/todolist';
import {TodoStore} from './store/todoStore';

bootstrap(ToDoList, [TodoStore]);

That was nice and simple! Under-the-hood this makes use of the Angular 2 Injector, which manages construction of these objects and maintains the singleton instances. You can also customise the injector in order to make your code more testable.

Adding items

Now that we have a store we can actually start adding todo items to it. Removing the fake data and including a method for adding new items results in the following:

export class TodoStore {
  items: TodoItem[];

  constructor() {
    this.items = [];
  }

  addItem(newItem: String) {
    this.items.push({
      text: newItem
    });
  }
}

The component can now use the addItem method on the store to add new items, each time clearing the newItem property in order to clear the text input area (thanks to the two-way binding):

addItem() {
  this.store.addItem(this.newItem);
  this.newItem = '';
}

See it in action via this commit.

At this point you might be wondering how Angular knows how and when to update the UI? With Angular 1.x this relied on the concepts of scopes and digests. While these just work for very simple scenarios, if you’ve done anything non-trivial such as updated the application state based on a HTTP response, or had to perform logic as a side-effect of a state change, you’ll have had to become quite familiar with digest cycles!

With Angular 2 things are quite different, firstly there is no need to manually trigger a digest. Angular 2 makes use of zonejs, a smart little library that provides an execution context that spans multiple asynchronous tasks. This is achieved by monkey patching the browser’s various asynchronous APIs. Zonejs also provides the opportunity to execute logic after each VM turn (i.e. each time the JavaScript engine executes some logic as a response to some event), which is what it is used for in Angular 2.

Secondly, the way Angular 2 handles updating the UI is fundamentally different to Angular 1.x, rather than propagating changes upwards, Angular 2 always propagates changes down from the root. You’ll see the impact of this design shortly.

Creating child components

The current application only has a single component, the root. More complex applications are likely to have numerous components, so in order to make this example a bit more realistic we’re going to refactor a little, adding a component that renders each individual todo item.

The HTML for the new component is quite simple:

<div class="view">
  <label></label>
</div>

As is the component:

import {Component, Input} from 'angular2/core';
import {TodoItem as TodoItemModel} from './../store/todoStore';

@Component({
  selector: 'todo-item',
  templateUrl: 'app/todoitem/todoitem.html',
  styleUrls: ['app/todoitem/todoitem.css']
})
export default class TodoItem {
  @Input()
  item: TodoItemModel;
}

The one new concept here is the Input decorator, which indicates that the property is part of the components public API and can be bound to. Notice the aliased module import {TodoItem as TodoItemModel}, this is because I gave both the model / store object and component the same name, I’m still getting to grips with this pattern ;-)

The component can be used as follows (after adding it to the directives property of the todo-list Component decorator):

<li *ngFor="#item of store.items">
  <todo-item [item]="item"></todo-item>
</li>

As a result of adding the Input decorator to the component’s item property it can now be bound to.

As part of the refactor, the CSS which relates to the todo item is also moved into the new component, because the CSS is scoped on a per-component basis.

With this refactor complete, the application works as previously, as seen in this commit.

Adding a delete button

The current example shows how to pass data into a component instance, via a property binding, but how do you propagate changes in the other direction? If you consider the component to be just like any other DOM element, then it should expose events in order to indicate changes in state.

In order to illustrate this, the todo item template is updated to add a ‘delete’ button, with a click event binding:

<div class="view">
  <label></label>
  <button (click)="doneClicked()" class="destroy"></button>
</div>

The component is updated to add an Output property which is an EventEmitter, with the doneClicked method firing this event:

import {Component, Input, Output, EventEmitter} from 'angular2/core';
import {TodoItem as TodoItemModel} from './../store/todoStore';

@Component({ ... })
export default class TodoItem {
  @Input()
  item: TodoItemModel;

  @Output()
  done = new EventEmitter();

  doneClicked() {
    this.done.next(this.item);
  }
}

The done event exposed by this component can then be bound to just like any other DOM element event:

<todo-item [item]="item"
  (done)="removeItem($event)">
</todo-item>

The todo list component handles this event and informs the store that the given item has been deleted. See the complete code in this commit.

As a result items can now be added or deleted from the list:

Change detection

Back to change detection, previously I mentioned that Angular 2 always propagates changes down from the root. What does this mean in practice? When the browser’s JavaScript engine executes any logic, (perhaps as a result of a timeout, DOM event or HTTP request), Zonejs ensures that the change detection logic is executed afterwards. This logic starts at the root component, checking for changes in state and re-rendering any whose state has changed.

You can actually see this process in action by detecting when the framework accesses the properties on your model objects.

By modifying the TodoItem class you can detect when the text property is read:

export class TodoItem {
  _text: String;

  get text() {
    console.log(`getting value for text "${this._text}"`);
    return this._text;
  }
  set text(value) {
    this._text = value;
  }

  constructor(text: String) {
    this._text = text;
  }
}

(Property getters / setters are a draft specification that is part of ES2016)

With the above logic in place, and a few items added to the todo list, you see a flood of console log messages every single time a key is pressed (within the todo input element):

This might seem frighteningly inefficient, however, the Angular team have stated that the change detection system can execute hundreds and thousands of simple checks within just a few milliseconds. It is well worth keeping this in mind when developing Angular 2 applications, your property getters are going to be executed a lot so make sure that they are simple (which is most often the case).

Immutability

With Angular 2 each component has its own change detector, which allows you to choose from a number of change detection strategies on a per-component basis (in much the same way that individual components can choose their own encapsulation mode). One of the most interesting change detection strategies relates to immutability.

If the rendered state of a component only depends on its input properties (which should be the case), then if these properties have not changed from one VM turn to the next, then the component doesn’t need to be re-rendered.

In the current application the TodoItem model object is immutable, so we can take advantage of this feature. Selecting a strategy is as simple as adding a changeDetection property to the Component decorator:

import {ChangeDetectionStrategy} from 'angular2/core';

@Component({
  ...
  changeDetection: ChangeDetectionStrategy.OnPush
})
export default class TodoItem {
  ...
}

Now re-running the application, you should not see the getters for each todo item being accessed on each keystroke:

This is because the ChangeDetectionStrategy.OnPush strategy indicates the the inputs (via property bindings) to the TodoItem component are immutable. The change detector is able to determine that the property values have not changed between keypresses, and as a result it does not need to invoke the getters on the underlying model items to determine whether their state has changed.

The pluggable nature of the change detection strategy means that you can use different approaches to inform Angular of what has changed in order to optimise rendering. One other option is the use of RX observables as described in this blog post.

Personally I really like the use of immutable objects, and will certainly be looking to use this change detection strategy as much as possible in my future applications.

Summary

Angular 2 is significantly changed from Angular 1.x, from my perspective it is a completely different framework. A simpler, more elegant framework.

It’s interesting to see the concepts that many of us were first introduced to with ReactJS, components and unidirectional propagation of changes, emerging in Angular 2. I’m looking forward to seeing how Angular 2 might integrate with other libraries such as immutable and flux. There are interesting times ahead …

Hopefully this step-by-step introduction to Angular 2 has been useful? If you have any questions or feedback, please use the comments section below.

And remember, all the code for this project is on GitHub.

Regards, Colin E.

MORE BY COLIN

blog comments powered by Disqus