Angular.js

First steps with Angular 2: 8 cool features

I’ve been doing some work the last couple of weeks with Angular2. I really like it. Not just because it uses typescript, but also because it feels really natural and straightforward while working with it. No more string based dependency injection, or strange digest cycle stuff, it just seems to work. This last week I’ve migrated our beta-13 Angular app to the latest rc-1, and used that to keep track of the fun and easy stuff Angular 2 provides. Note though, that the application we’re developing is really that complex, so I can only assume we’ll run into more complex Angular2 features in the near future. For now, though, let me share some general tips and tricks we’ve encountered thus far (in no particular order). Oh, all examples are in typescript, since after using that, I really don’t want to go back to plain old javascript (POJS?).

1. Pipes for formatting

If you need to format some output string, in the old Angular version you used Filters. In Angular 2, though, the name of filters have changed and they are now called pipes. Using pipes is very easy and only takes a couple of steps to get working. First, you need to define a pipe:

import {Pipe} from '@angular/core';
import {FormatUtils} from '../utils/formatUtils';
 
@Pipe ({
  name : "humanReadableNumber"
})
export class HumanReadableNumber {
 
  transform(value: any) {
    // converts a size to something human readable
    return FormatUtils.humanSize(+value);
  }
}

Now that we’ve got a pipe we can import it in our component, and use it to format a value in our page. We import is like this, and add it to the pipes property of the

import {Component} from '@angular/core';
import {CORE_DIRECTIVES} from '@angular/common';
import {HumanReadableNumber} from '../../../shared/pipes/humanReadablePipe'
 
@Component({
	templateUrl: 'some-template.html',
  directives: [CORE_DIRECTIVES],
  pipes: [HumanReadableNumber]
})
export class SomeCmp {
  ...
}

And using it in our code is just as simple:

<div class="col-sm-3">
  <span class="text-red">{{failureCount | humanReadableNumber}}</span>
</div>

And the cool thing is you can chain pipes together, and of course also create parameterized pipes. The official documentation for pipes can be found here: https://angular.io/docs/ts/latest/guide/pipes.html

2. HTTP is easy to use

You can’t have a web application without making some HTTP calls to pull in data. With Angular 2 this is suprisingly easy. Just inject the **http** component, pass in the headers and parameters, and you get back on observable. As soon as you subscribe to the observable, the call will be executed, and you can easily use the functions provides by rx.js to transform the response (see next tip):

private getSomeData(timePeriod: TimePeriod): Observable<any> {
 
  let headers = new Headers();
  headers.append('Content-Type', 'application/json');
 
  // handle the today case differently, since we need to go to start of day
  let [from, to] = (timePeriod.name === Configuration.timePeriod.today.name)
    ? [moment().utc().hours(0), moment()]
    : [moment().subtract(timePeriod.amount, timePeriod.period), moment()];
 
  // setup and add the query parameters
  let params = new URLSearchParams();
  params.set('environment', 'test');
  params.set('interval', timePeriod.period);
  params.set('from', from.toISOString());
  params.set('to', to.toISOString());
 
  return this.http.get('http://the.url', { headers: this.getHeaders(), search: params })
    .map((res: any) => res.json());
}

And the result is an observable, which can be consumed like this:

someService.getSomeData(period).subscribe((json: any) => ({// do something with the json}));

The official documentation for HTTP can be found here: https://angular.io/docs/ts/latest/guide/server-communication.html

3. Observables are really cool

In Angular 1 when communicating over HTTP, or doing stuff asynchronously you would use promises. In Angular 2, promises have been replaced with Observables. Without going too deep into what Observables are, in short they provide a way to subscribe to a sequence of events and act whenever the observable emits a new event. The really cool thing is that you can apply all kinds of operations and transformations on the events before they are processed by a subscriber. You can for instance map it to a domain object, group multiple events together and much, much more (see the rx.js documentation for all the options: http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html)

The easiest example of an Observable is when you want to do an HTTP request in Angular 2. Instead of working with promises you now do something like this:

import {Http} from '@angular/http';
import {Inject} from '@angular/core';
import {Headers} from '@angular/http';
 
export class SomeService {
 
  constructor(@Inject(Http) private http: Http) {};
 
  getPerson() : Observable<Person> {
    let headers = new Headers();
    headers.append('Content-Type', 'application/json');
 
    return this.http.get('http://the.url.endpoint', {headers})
      .map(res => res.json())
      .map(res => new Person(res.name, res.age));
  }
}

As you can see the result is an Observable. We can now use this Observable very simply like this:

import {Component} from '@angular/core';
import {Person} from '../shared/domain';
import {SomeService} from 'some-service';
 
@Component({
  templateUrl: 'someTemplate.html'
})
export class AnotherCmp {
 
  public person: Person;
 
  constructor(private someService: SomeService) {
    someService.getPerson().subscribe((person) => (this.person = person));
  }
}

We subscribe to the observable. When we do this, the HTTP call will be made, and when it returns, we just assign the response to a public variable. Another feature of Observables we use is to wait for multiple calls to finish, before sending the message to a subscriber. This uses the forkJoin function provided by rxjs.

public getMonthlyInfo(): Observable<MonthData[]>  {
  let observables: Observable<MonthData>[] = [];
 
  // create the observables for the different calls
  // and add them to the observables array
  ...
 
  // Intellij throws a false error: https://youtrack.jetbrains.com/issue/WEB-20992
  // this returns another observable, which returns an array of results from the
  // observables we passed in to the forkJoin
  return Observable.forkJoin(observables);
}

And this is just touching upon the surface of what is possible with Observables. On a side note, if you prefer to work with Promises, you can use the *toPromise* function on an observable to turn it into a promise.

4. Start with use angular2 seed

Maybe I should have added this as the first tip, but setting up a build environment using gulp systemJS (or webpack) can be a bit of an hassle. Luckily, though, there is an easy way to get started using angular seed. There are multiple angular seeds available, but I use this one: https://github.com/mgechev/angular2-seed. It provides a systemJS / gulp based seed setup, including all kinds of nice features. Getting started with it, is also very trivial:

git clone --depth 1 https://github.com/mgechev/angular2-seed.git
cd angular2-seed
# install the project's dependencies
npm install
# watches your files and uses livereload by default
npm start
# api document for the app
npm run build.docs
 
# dev build
npm run build.dev
# prod build
npm run build.prod

If you want to use it as the base for your own project, it might be good to run the following git commands after cloning the repository, to point it to your own repository / project:

rm -rf .git
git init
git remote add origin https://giturl.of.your.project.git

5. Intercept requests for authentication

One of the first things I wanted to add to our application was a way to check whether a user was logged in. I was looking for global filters, and stumbled upon this example: https://github.com/auth0-blog/angular2-authentication-sample. This shows how to use a RouterOutlet to check for certain conditions, before handling the request. Basically what you do is, you define a custom RouterOutlet:

From (https://github.com/auth0-blog/angular2-authentication-sample/blob/master…):

import {Directive, Attribute, ViewContainerRef, DynamicComponentLoader} from '@angular/core';
import {Router, RouterOutlet, ComponentInstruction} from '@angular/router-deprecated';
import {Login} from '../login/login';
 
@Directive({
  selector: 'router-outlet'
})
export class LoggedInRouterOutlet extends RouterOutlet {
  publicRoutes: any;
  private parentRouter: Router;
 
  constructor(_viewContainerRef: ViewContainerRef, _loader: DynamicComponentLoader,
              _parentRouter: Router, @Attribute('name') nameAttr: string) {
    super(_viewContainerRef, _loader, _parentRouter, nameAttr);
 
    this.parentRouter = _parentRouter;
    // The Boolean following each route below
    // denotes whether the route requires authentication to view
    this.publicRoutes = {
      'login': true,
      'signup': true
    };
  }
 
  activate(instruction: ComponentInstruction) {
    let url = instruction.urlPath;
    if (!this.publicRoutes[url] && !localStorage.getItem('jwt')) {
      // todo: redirect to Login, may be there a better way?
      this.parentRouter.navigateByUrl('/login');
    }
    return super.activate(instruction);
  }
}

And this allows you to check certain conditions before normally processing the request, or passing it on. I can explain how it works here, but just look at the github repository which will do a much better job.

6. Directives are very easy to write

In Angular2 the distinction between controllers and directives is removed. A directive is just a simple component, where some of it’s values are populated when the component is instantiated. For instance a simple component that wraps one of the great https://github.com/valor-software/ng2-charts charts looks like this:

import {Component, Input} from '@angular/core';
import {CORE_DIRECTIVES} from '@angular/common';
import {CHART_DIRECTIVES} from 'ng2-charts/ng2-charts';
import {DecisionNodeSummary} from '../../../shared/domain/dagSummary';
 
@Component({
  selector: 'dag-decnode-graph',
  templateUrl: 'decisionnode-graph.html',
  directives: [CHART_DIRECTIVES, CORE_DIRECTIVES]
})
export class DagDecNodeGraph {
 
  public title = "";
 
  @Input()
  set nodeSummary(nodeSummary: NodeSummary) {
    this.routingStatusChartData = [nodeSummary.falsePath, nodeSummary.truePath];
    this.title = nodeSummary.name;
  };
 
  public routingStatusChartLabels: string[] = ['false', 'true'];
  public routingStatusChartType: string = 'doughnut';
  public routingStatusChartLegend: boolean = false;
  public routingStatusChartData: number[] = [0, 0];
  public routingStatusChartOptions: any = {};
 
  constructor() {
  }
 
}

And the relevant template:

<div class="col-md-4">
  <h4 style="text-align: center">{{ title }}</h4>
  <base-chart class="chart"
              [data]="routingStatusChartData"
              [labels]="routingStatusChartLabels"
              [legend]="routingStatusChartLegend"
              [chartType]="routingStatusChartType"></base-chart>
</div>

Nevermind the model which is passed in, but this is basically all you need to do to define a directive. To use this directive in a page you just do something like this:

<div *ngFor='let node of nodes'><dag-decnode-graph [nodeSummary]="node"></dag-decnode-graph></div>
</div>

How easy is that! No more specific javascript, or custom functions. Just a simple component, just like all the other components.

7. Typescript: domain objects are cheap and easy, type everything.

I couldn’t write this, without at least mentioning typescrypt. I really like typescript I come from a Java and Scala background and am used to having compile-time typechecking (and using IDEs to autocomplete some stuff). With typescript this also becomes available to Angular, and it works really great. What makes it even better is that creating basic domain objects is really easy and quick:

export class Person {
  constructor(public name: number, public age: number) {}
}

And you can easily access the public properties, and hide the private properties. It even support default values. It isn’t quite up to Scala case classes yet, but it is a great way of defined a good domain model in your frontend applications. This combined with the **map** function of the **Observable**, it is very easy and convient to use these domain objects throughout your application.

8. Use jquery

In many cases there isn’t a big need for jQuery in Angular projects. However, there is big ecosystem of great jQuery based components and libraries that will come in handy. While using jquery in Angular 2 isn’t that difficult, getting the typescript compiler to compile your code when including jquery in your dependencies might be harder. When you include jQuery you’ll quickly run into an error message something like this:

Subsequent variable declarations must have the same type.  
Variable '$' must be of type 'cssSelectorHelper', but here has type 'JQueryStatic'.
Web\Scripts\typings\jquery\jquery.d.ts   3936

Basically, there are two different libraries claiming the $ name. This can be quickly solved by replacing the last couple of lines from the **jquery/index.d.ts** to this:

interface JQuery {
    // add additional functions you might want to expose from other libraries
    // that append the jquery object
    chosen(options?:any):JQuery;
    bootstrapTable(...options:any[]):JQuery;
}
 
declare module "jquery" {
    export = JQuery;
}
declare var jQuery: JQueryStatic;

Now you can use the jquery library like this in code:

declare var jQuery:JQueryStatic;
 
...
// do whatever you want, just use jQuery instead of $
jQuery('.elem')

While this works, you’ll notice that you have to repeat this process after each npm install or whenever you add a new library. Luckily you can override the typings that are retrieved when doing an **npm install**. So to permanently apply these settings, save the typings to a separate directory and change the jquery entry in the **typings.json** file to this:

"jquery": "file:overrides/typings/jquery/index.d.ts",

This points to our own custom jquery typings file in a separate directory. finally to get everything to work also update the tsconfig.json file to exclude this file or else you’ll get duplicate definitions errors:

"exclude": [
  "node_modules",
  "dist",
  "typings/browser.d.ts",
  "typings/browser",
  "src",
  "overrides"
]

Conclusions

This is just a quick set of observations from a couple of days intensive Angular 2 development. My main conclusion, though, it that it really is fun doing frontend development with Angular 2. It might because of typescript, but so far it feels like a really great, well thought out framework. It just works and a lot of the annoying things from Angular 1 have been solved.

Reference: First steps with Angular 2: 8 cool features from our WCG partner Jos Dirksen at the Smart Java blog.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button