Angular 2 Beta 0 (somnambulant-inauguration) lands – Small App with Rxjs, TypeScript

by
Tags: , , , ,
Category:

Editor’s note (well, poster’s anyway) – the word is somnambulant, and was corrected in a further commit… Changing the URL and title, though wordpress will keep the old link too… Thanks to Dave Esterkin for catching this!
A big announcement came last week when the Angular 2 team released Angular 2, Beta 0, a.k.a. sonambulent-inauguration.
This is the first release where the Angular 2 API is becoming production ready and somewhat frozen in terms of new features.
Keep in mind the Angular team has been working out in the open for a long time on Angular 2 – it was first announced that there would be real working Alphas back in March at ng-conf 2015. For a brand-new rewrite of Angular 1, this has had significant eyeballs on it for a long time, and has even gone through some significant rework once Google put the API in front of its’ own teams.

An example

This admittedly lame example shows a fully functioning Angular 2 application using the Http and RxJs libraries, and a simple mapping operation. I wanted to start as bare-bones as possible to illustrate the basic features.

A TypeScript class for our data

I’ve taken the effort to build a data class, BlogEntry which handles creation of each element as well as parsing of elements using a static helper method.
First, let’s look at that class:

class BlogEntry {
    title: string;
    content: string;
    constructor(title:string, content:string) {
        this.title = title;
        this.content = content;
    }
    static asBlogEntries(jsonArray: Array<Object>) {
        return jsonArray.map((datum) => BlogEntry.asBlogEntry(datum));
    }
    static asBlogEntry(json: Object) {
        return new BlogEntry(json["title"], json["content"]);
    }
}

You can see above that we can provide a constructor that formally takes two parameters to construct a blog entry. But we also want to take a general object, perhaps created by JSON, and coerce it into a blog entry as well. For that we’ve created a static method, asBlogEntry, which calls our constructor.
Next, we provide a collection-based method, asBlogEntries, which takes an object array, and uses the index accessor to dynamically request the fields in question. There may be a better way to do this in TypeScript, but this works for our example.

Generating a simple component – the view

If we’re displaying a blog roll, and we want it to decode the HTML of the entries as the body, we’d approach it something like this (forgive the ugly content, it’s just a demonstration):

<div *ngFor="#blog of blogs">
  <h1>{{ blog.title }}</h1>
  <div [innerHtml]="blog.content"></div>
</div>

This little fragment uses Angular 2’s new directive syntax, which has changed in Beta 0 to using camel case for all directives both in JavaScript AND HTML. In my opinion this is a better thing than using the dashed lower-case syntax (which they are now calling Kabob case, but I’ve heard called snake case in the past).
The ngFor directive iterates a new div for each of the array elements in a field called blogs.
In order to get this component started, we need to import the appropriate objects. The declaration so far would look like this:

import {Component} from 'angular2/core';
import {CORE_DIRECTIVES} from "angular2/common";

Let’s wrap this template in a @Component decorator:

@Component({
    template:`
    <div *ngFor="#blog of blogs">
      <h1>{{ blog.title }}</h1>
      <div [innerHtml]="blog.content"></div>
    </div>
    `,
    selector: 'blog-roll',
    directives: [CORE_DIRECTIVES]
})

Now we just have to write a class that this decorator attaches to, and that provides a blogs object:

export class BlogRoll {
  blogs: Array<BlogEntry>;
  constructor() {
    this.blogs = [
      new BlogEntry("Blog1", "This is a real treat!"),
      new BlogEntry("Blog2", "This is not a real treat!");
    ];
  }
}

In this BlogRoll component definition, we provide a data object, blogs, which we have strongly typed as an array of BlogEntry objects. The constructor simply hard-codes the implementation as a simple array.

Introducing an Http API

Now, I’m not architecting the perfect solution here, and if I did, I would definitely externalize the next thing into a service. For a hello world, using the Http API in the constructor will suffice. A follow-on blog entry will show you how to create an injectable service.
The Http API in Beta 0 uses the initial beta release (also beta 0) of the Microsoft Reactive Extensions project, version 5.0. Google hitched their wagon this year to Microsoft for the Typescript compiler, and here too, they’re using another Microsoft-sponsored API.
To incorporate an AJAX fetch into our class, so that we can load the data externally, we have to import some additional classes and functions:

import {Http, Response, HTTP_PROVIDERS} from "angular2/http";
import 'rxjs/add/operator/map';

The Http object class is the facade for Angular 2’s networking API. Answers are brought back as Response objects. The HTTP_PROVIDERS array indicates which classes must be referenced by the component in order for it to use the Http API.
Another key import is the operator used by rxjs. The Microsoft Reactive Extensions library has many operators including map, reduce, filter, fold and more. See a list at github.com/ReactiveX/RxJS/tree/master/src/operator.
To get this to work, we need to update our component properties, so in the @Component annotation, add the following line:

directives: [CORE_DIRECTIVES]

More specific imports

By now, prior alpha developers will notice we can’t get away with angular2/angular2 for imports anymore. This is by design – they want you to use the package names so you understand where the classes are coming from, and this means you need to spend some time on the API page searching for components and observing their package locations.

Loading the data from Http

The next part would ideally be spread across a service and our component. But if you’re used to Angular 1’s promises API, this example will give you all of the code that equates to an $http service call with a promise result.
Instead, we’re using rxjs Observables and operators. We start off with the fetch:

  http.get('/data/data.json')

which can execute the fetch to get the JSON data of some test blog entries from a file on the server.
Next, we chain that to a map operator, which takes the response and transforms it into an object of your choice. In this case, we’re delegating our mapping to the static, collection-based asBlogEntries function in BlogEntry:

.map((res: Response) => {
   return BlogEntry.asBlogEntries(res.json())}
)

To use this API, we have to subscribe to the results. In our subscription, which ideally lives in our component, we grab the data and place it into the blogs collection, which we strongly typed earlier as Array<BlogEntry>:

.subscribe(
  (data: Array<BlogEntry>) => {
     console.log('data is', data);
  },
  (error: Object) => {
     console.log('error!', error);
  }
);

Lots of samples are leaving out the second (and I’m leaving out the third) function call in subscribe, but they are:

  • Data delivery callback
  • Error callback
  • Completed callback

I’ll have a lot more to say about this API in some upcoming blog posts, including comparing it to a promise API, how to refresh results periodically, how to cancel responses, and how to split it between a service and a caller such as a @Component

Bootstrapping the outer app

The app needs to be loaded using Angular’s bootstrap API call. So we’ll use another component as our launching point, one called App:

import {Component} from 'angular2/core';
import {BlogRoll} from './blog/blogroll';
@Component({
  template: `
   {{ message }}
   
  `,
  selector: 'app',
  directives: [BlogRoll]
})
export class App {
  message: String;
  constructor() {
    this.message = 'Hi there!';
  }
}

Note in this class, we’ve imported our BlogRoll component and can mount it as a tag, using the blog-roll selector in the BlogRoll class. We have to load that class using the directives array, after importing it into our JavaScript file with the import command.
There is a big push out there to use embedded templates for most components. For many cases it makes sense. If you don’t fall in love with it, then just use templateUrl as per Angular 1, but the code and template stay near each other, ala React, which many developers find extremely useful.

The index.html file

There are at least a dozen starter apps out there, some of which are more complete than others. In this example I was going for the bare bones version, assuming we’ll do transpilation in the HTML itself for development reasons.
I’m still hacking on this, and the Angular team really owes us some definitive examples with testing included, with good documentation. So forgive me if I go a bit crazy here. But for now, realize I’m loading Angular using System.JS, and referencing the code in this sample using the URLs, but for now, I’ve just used the Plunker tool (http://plnkr.co) to create a 2.0.0.beta 1 project, then used wget to freeze the libraries into a lib directory for simplicity.
Here’s the HTML file in question:

<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="style.css">
    <script src="lib/system.js"></script>
    <script src="lib/typescript.js"></script>
    <script src="lib/angular2-polyfills.js"></script>
    <script src="lib/Rx.js"></script>
    <script src="lib/angular2.dev.js"></script>
    <script src="lib/http.dev.js"></script>
    <script src="lib/router.dev.js"></script>
  </head>
  <body>
     <script>
      System.config({
        transpiler: 'typescript',
        typescriptOptions: { emitDecoratorMetadata: true },
        packages: {'src': {defaultExtension: 'ts'}}
      });
    </script>
    <script>
      System.import('angular2/platform/browser').then(function(ng){
        System.import('src/app').then(function(src) {
          ng.bootstrap(src.App);
        });
      });
    </script>
    

Hello Plunker!

Show me when I don't load! </body> </html>

I’ve chosen to pre-load the Angular 2 router library as well as our Http library. This way we can experiment with a few of the other APIs as we build this example up.
Note the bootstrapping for this application, which uses the library-agnostic SystemJS loader, uses a promise to bootstrap the Angular 2 app, named App, after the loader loads the actual src/app TypeScript file and compiles it. This is due to the System.config block above, which transpiles the TypeScript loaded with the .ts extension and outputs it as valid ECMAScript 5 JavaScript.
This is another area where there are a lot of options, and we’ll discuss the various merits of running Angular in ES 5 directly (and skipping TypeScript), using ES6 and the ES6 shims to load in ES6 module format directly, or in our case, using TypeScript and transpiling while loading the page. Of course, this example is set up purely for development and experimentation.

Getting the Source and Running the Application

If you want to play with this sample, head over to github.com/krimple/angular2-simple-ts-http-example, download the zip or clone the project, and issue the following commands:

cd angular2-simple-ts-http-example
npm install -g live-server
npm install -g typescript@1.7.3
npm install
live-server

then browse to http://localhost:8080 – you should get a simple little blog listing that proves we’re using Http to fetch the entities from data/data.json.