Open Credo

July 8, 2015 | Software Consultancy

A deep dive into Angular 2.0

I was quite excited around autumn last year when Google started to work on a new version of Angular (Angular 2.0) which promised to revolutionise web development. There were rumours that Angular 2.0 wouldn’t be backward compatible with its predecessor, and would be written in Google’s AtScript which is a JavaScript based language on top of Microsoft’s TypeScript. The lack of backwards compatibility raised some concerns, especially for the clients that we had used Angular at. But, lets not get ahead of ourselves here….

WRITTEN BY

Norbert Annus

A deep dive into Angular 2.0

Introducing TypeScript

In May 2015, Google released a developer preview version (pre-beta) of Angular 2.0, which I was lucky enough to look at before it reached its ‘cool’ status. As it turned out Google’s partnership with Microsoft meant Angular 2.0 was now to be built using TypeScript, instead of the previously hyped AtScript;  welcome move as having two approaches would have fragmented the community. At the time of writing, Angular 2.0 is being built on TypeScript 1.5, which is still in beta.

So, what are the benefits of using TypeScript instead over plain old JavaScript?

First off, there is no need to use TypeScript, you could use Dart or ECMAScript 5/6 instead. With language features that include runtime type introspection and metadata annotation on class declarations which is a good way to separate code from the information, the real question is: why wouldn’t you use TypeScript?. Type definitions for the compiler and the IDE are available from a repository called DefinitelyTyped. Browsers, however, don’t understand TypeScript, so it must be compiled into browser-compliant JavaScript to work.

The Project

The goal is to create an Angular 2.0 based book-store application implemented in TypeScript, using Bootstrap for the layout and communicating basic CRUD operations via a REST API calls to an ExpressJS server.

For the build process I will use Gulp which first compiles the TypeScript code and styles and then copies the compiled code to a destination directory accessible through a local web server.

I will assume you’ve already installed npm and Node.js on your machine.

DISCLAIMER: The platform is still in developer preview state and some of the libraries used are still in beta. Therefore, parts of the code may become obsolete over time.

Basic setup

Create an empty directory on your machine, which will be the starting point for all the upcoming commands.
To handle dependencies like Bootstrap and Traceur we’re going to use bower, where the latter one compiles the next generation JavaScript code into browser-compliant JavaScript.

npm install -g bower //if you haven’t installed bower yet
bower init
bower install bootstrap traceur-runtime --save-dev

We’re going to use the node package manager to install TypeScript definitions and the TypeScript compiler as well:

npm install -g tsd
npm install -g typescript@^1.5.0-beta

We’ll need to fetch the type definitions for Angular 2.0 and for all of it’s dependencies. By default, this will create a directory called typings from which the definitions can be referenced by TypeScript.

tsd query angular2 --action install --save
tsd query es6-promise rx rx-lite --action install --save
tsd query whatwg-fetch --action install --save

An additional _custom directory has to be created within typings which will include some scaffolding stuff. You’ll have to copy content over from this project’s git repository and reference the custom.d.ts file in your tsd.d.ts file within your typings folder.

///

Let’s create our package.json file and pull all the node dependencies (TypeScript, ExpressJS, gulp & plugins) that we’ll need for this project:

npm init
npm install typescript --save-dev
npm install body-parser express morgan --save-dev
npm install gulp del gulp-less gulp-live-server gulp-plumber gulp-rename gulp-sourcemaps gulp-tslint gulp-typescript run-sequence --save-dev

Next  create a gulpfile.js with all the tasks that need to be executed to create the build. I’m going to use an external configuration file called gulpfile.config.js to set up the different variables for my streaming build system. I will highlight the important parts of them later.

Your final directory structure should look like the following, where the src/webapp will contain the TypeScript source files, LESS files and index.html and src/server will contain the entry point for your ExpressJS server and the mocked data. The public folder and it’s content will be generated by gulp:

Angular Project directory structure

Project directory structure

The Compilation

I’m not going to walk you through all the details regarding the content of the gulp files but there are some parts I should highlight as they caused some headaches for me at first.

In our compile-td task we need to set up the compiler properly, otherwise it will fail:

var tsc = require(‘gulp-typescript'),
    typescript = require(‘typescript’);

tsc({
    typescript: typescript, // use TypeScript 1.5.0-beta
    module: ‘commonjs’, // use the CommonJS library
    target: ‘ES5’, // ECMAScript 5
    emitDecoratorMetadata: true, // for annotations
    declarationFiles: false,
    noExternalResolve: true
})

NOTE: If you’re using IntelliJ 14.1+ then you can use it’s built-in TypeScript compiler with a custom compiler path: /usr/local/lib/node_modules/typescript/bin. Command line arguments for the compiler can be set up at Preferences > Language & Frameworks > TypeScript > Command line options, which must be: -t ES5 –module commonjs -emitDecoratorMetadata

The Entry Point(s)

The entry point for our web application is a simple index.html which is cleaner and simpler than we used to have in Angular 1.x:


   
      
      
      

      
   
   
      Loading ...
      
      
   

In addition to the core Angular 2.0, its separate router and the earlier mentioned traceur libraries, we also need to include an open-source, third-party library called System.js which provides ES6 module loading functionality to browsers. This is as simple as passing the relative path of the compiled main JS file in the System.import function.

You might notice that the tags within the body resemble a custom directive from Angular 1.x restricted to HTML elements, but they’re not. It’s a Component selector in the new world of Angular 2.0, which encapsulates all logic related to the component itself. If you’re familiar with the concept of directives in Angular 1.x then you will like the component based architecture of the new platform.

Let’s have a look at on our Angular 2.0 entry point, the bootstrap.ts:

/// 

import {bootstrap} from 'angular2/angular2';
import {routerInjectables} from ‘angular2/router';

import {App} from './pages/pages';

bootstrap(
   App,
   [
      routerInjectables
   ]
);

The module loading mechanism from Angular 1.x is gone. What we have instead is an import statement that we can use to cherry-pick which required symbols will be loaded from a specific type definition at runtime. We need to reference the type definitions that we’re going to use in our source files and  the bootstrap() function helps to load our new component into its page. It’s first parameter is a @Component annotated Type which serves as the root component of our application, the second is an additional set of bindings to override the default behaviour.

A Component

What is a Component? How is it structured? Let’s consider the skeleton of our App component:

import {Component, View, coreDirectives} from 'angular2/angular2';
import {RouteConfig, RouterOutlet, RouterLink, Router} from 'angular2/router';

@Component({
   selector: 'app',
})

@View({
   directives: [ RouterOutlet, RouterLink, coreDirectives ],
   template: `

` }) export class App { title: string; constructor() { this.title = ‘Angular 2 CRUD Application’; } }

The @Component annotation defines the HTML tag for our component using a CSS selector. Do you remember the tag in our index.html? That’s where it comes from. The @View annotation defines the HTML template that the component is based on. Here we can define the set of directives that we’re going to use within our template. There are multiple ways to define the template itself:

  • Using the template property and the new grave accent symbol (“) to define a multi-line template to be more human readable
  • Using the template property and single quotes to define an inline template
  • Using the templateUrl property to reference an external HTML relatively

Controllers and $scope have changed form in Angular 2.0. The component’s class is responsible for controlling the behaviour of the component, and its member variables and functions are subject to data bind in the underlying template. It is worth noting that the new platform does not use two-way data binding, which was a real advantage in its predecessor. The reason for leaving this out is mainly because of performance. The resolution which takes place during the data bindings are assigned performance costs, and in a fast and smooth framework this is something to avoid. As yet it is not clear how two-way data binding will be replaced within Angular 2.0.

Forms

Angular 2.0 Form Builder module makes it easier than ever to build forms; you can create several ControlGroups within a form where each group consists of one or more Controls. For each Control there is a unified way to define its basic properties like default values or assigned Validators. Let’s have a look at this in our CreateBook component:

import {Component, View, Directive, coreDirectives} from 'angular2/angular2';
import {formDirectives, FormBuilder, Control, ControlGroup, Validators} from 'angular2/forms';

@Component({selector: 'create-book'
   selector: 'create-book'
})

@View({
   templateUrl: './app/pages/create/create-book.html',
   directives: [coreDirectives, formDirectives]
})

export class CreateBook {
   createBookForm: ControlGroup;
   isbnInput: Control;
   titleInput: Control;
   authorInput: Control;
   publicationDateInput: Control;
   
   book: Book;

   constructor(public formBuilder: FormBuilder) {
      this.book = new Book();
      this.createBookForm = formBuilder.group({
         'isbn': ['', Validators.required],
         'title': ['', Validators.required],
         'author': [''],
         'publicationDate': ['']
      });

      this.isbnInput = this.createBookForm.controls.isbn;
      this.titleInput = this.createBookForm.controls.title;
      this.authorInput = this.createBookForm.controls.author;
      this.publicationDateInput = this.createBookForm.controls.publicationDate;
   }
}

For the sake of simplicity I will demonstrate the bindings for a simple input control in the underlying template:

[ng-form-model]=”createBookForm” class=”form-horizontal” role=”form” novalidate>

This field is required.

 


The HTML markup looks a bit weird at first glance. Instead of using ng-* we’re using a new [] or * syntax for directives:

  • [class.has-error]=“expression” instead of ng-class={‘has-error’: expression}
  • [(ng-model)] instead of ng-model
  • *ng-if instead of ng-if

In terms of form functionality:

  • [ng-model-form] binds the ControlGroup domain model to the form
  • [ng-form-control] binds the Control domain model to the form control, so the form field can be referenced via its name and we don’t need to use name attributes chained to the form’s name in every case
  • touched is the new dirty flag

Routing

It looks like the routing concept has been revamped as well. Instead of configuring routes through the $routeProvider, we can simply set up a @RouteConfig as follows in our main component:

@RouteConfig([
   { path: '/list',     as: 'list',   component: ListBooks },
   { path: '/create',   as: 'create', component: CreateBook },
   { path: '/edit/:id', as: 'edit',   component: EditBook },
   { path: '/view/:id', as: 'view',   component: ViewBook }
])

A configuration item takes 3 parameters:

  • path – where you want to link to
  • as – string identifier what can be used in the router-link directive:
Create
  • component – the component’s class which needs to be loaded upon route change

It should come as no great surprise that there is a replacement for our good old called and it serves as a placeholder, which Angular 2.0 dynamically fills in based on the current route.

From child components we can navigate to a different route through the following:

import {Router} from 'angular2/router';

export class ListBooks {
   constructor(public router:Router) {}

   viewBook(book) {
      this.router.parent.navigate('/view/' + book.isbn);
   }
}

When writing this, the API seemed buggy, so we need to manually go to the correct URI at application startup. I was also not able to load a route directly from the browser’s URL, so we need navigate to different routes programmatically.

RESTful Web Services

At time of writing the HTTPAPI was still under construction so I had to be creative. According to the documented API, the HTTP class can be used to perform HTTP requests using XMLHttpRequests as the default backend. The class provides the usual member functions as follows:

  • request(url, options) – any type of HTTP request
  • get(url, options) – HTTP GET request
  • post(url, body, options) – HTTP POST request
  • put(url, body, options) – HTTP PUT request
  • delete(url, options) – HTTP DELETE request
  • patch(url, body, options) – HTTP PATCH request

Let’s have a look on our BookService class:

import {Http, httpInjectables} from 'angular2/http';

export class BookService {
   http: Http;
   baseURL: string;

   constructor(@Inject(Http) http) {
      this.http = http;
      this.baseURL = '/api/books';
   }

   _callAPI(url:string, method:string, data:any) {
      return window.fetch(url, {
         method: method,headers: {
         headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json'
         },
         body: JSON.stringify(data)
      })
   }

   getBook(id:string) {
      return this.http.get(this.baseURL + '/' + id);
   }

   updateBook(id:string, data:any) {
      return this._callAPI(this.baseURL + '/' + id, 'PUT', data);
   }
}

The GET and DELETE methods worked as expected. I hit problems when dealing with the Content-Type header for JSONs in the PUT and POST requests. Based on the documentation we need to merge our custom options ({ headers: { ‘Content-Type’: ‘application/json’ }}) to the requests BaseRequestOptions property but I ended up with the default Content-Type which is ‘text/plain’.

As a result I decided to try out the experimental Fetch API which is similar to XHR but it uses Promises, which should be familiar if you’ve worked with Angular 1.0. Its headers can be configured the same way and it did the trick. One thing to note is that I couldn’t see the request body itself in Chrome Developer Tools, but EsxpressJS does handle those requests properly.

We’ve already seen how to make requests but how to deal with the responses?

import {RouteParams} from 'angular2/router';
import {Inject} from 'angular2/di';

import {BookService} from '../../services/BookService';
import {Book} from '../../models/Book';

export class EditBook {
   book: Book;

   constructor(@Inject(RouteParams) params, public bookService:BookService) {
      this.book = new Book();
      this.getBook(params.get('id'));
   }

   getBook(id:string) {
      this.bookService.getBook(id)
         .map(res => res.json())
         .subscribe(res => this.book = Book.fromJSON(res));
   }

   updateBook_successHandler(response) {}

   updateBook_errorHandler(error) {}

   updateBook() {
      this.bookService.updateBook(this.book.isbn, this.book.toJSON())
         .then(response => this.updateBook_successHandler(response))
         .catch(error => this.updateBook_errorHandler(error));
   }
}

The response is an Observable object which we can pipe through different methods to, for example, map it to JSON format, or subscribe to the result. This way we can process the data itself. If we’re using the fetch function we can utilise the catch methods to deal with the response.

Conclusion

As a JavaScript developer there are lots of new things ‘under the hood’ to explore in Angular 2.0. Core concepts and syntax are changing, but overall the framework itself seems clearer to me than Angular 1.x. Having said that, the developer preview version lacks some core features and there are still lots of open questions.

Angular 2.0 is built upon bleeding edge technologies so it’s unlikely to become mainstream in the short term. Personally, I really look forward to the beta version and hope it will be production ready around late October 2015, when the ng-europe conference will be held.

The working project code can be downloaded from the GitHub repository.

 

This blog is written exclusively by the OpenCredo team. We do not accept external contributions.

RETURN TO BLOG

SHARE

Twitter LinkedIn Facebook Email

SIMILAR POSTS

Blog