Angular2 series – Component, Directive, Pipe and Service

This post is part of a series of posts about Angular2. You can find them all in the introduction: http://julienrenaux.fr/2015/11/30/angular2-series-introduction/


Before we start and to make sure we are in line on the terminology, I will use the name AngularJS to define AngularJS 1.x and Angular2 to define AngularJS 2.x.

Component

A component is what you used to call a directive in AngularJS. It contains a template, styles, a list of injectables (directives, services) and a selector.

Official docs

Let’s create a component that lists the US Democratic Party presidential candidates.

import {Component, View, NgFor} from 'angular2/angular2';

@Component({
    selector: "navbar",
    directives: [NgFor], 
    styles: [`
        li{
          color: gray;
        }
    `],
    template: `
        <h2>Democratic Party presidential candidates</h2>
        <ul>
            <li *ngFor="#item of items; #i = index">{{item}} {{i}}</li>
        </ul>
    `
})
export class Navbar {
    items: Array<String>

    constructor() {
      this.items = [
        "Hillary Clinton",
        "Martin O'Malley",
        "Bernie Sanders"
      ]
    }

    ngOnInit() {
        console.log('[Component] navbar ngOnInit');
    }
}

When a component is instantiated, Angular2 creates a shadow DOM for the component (Shadow DOM provides encapsulation for the JavaScript, CSS, and templating in a Web Component). Then, the template and styles are injected inside it.

Learn a bit more about Web component and specifically about the <template> tag by reading the previous article of this series of posts: Angular2 series – Template Syntax.

You can now use your component by inserting it into your html page:

<navbar></navbar>

Demo

Screen Shot 2015-12-12 at 19.32.27

http://embed.plnkr.co/cUCWoUDRzd31YRRbo5Rg/preview

Lifecycle hooks

In the previous example, we used the ngOnInit Class method to dump a message [Component] navbar ngOnInit in the console. It is called only when the component is initiated. It exists several hooks that make your life easier when it comes to plug yourself in between component life phases.

  • ngOnChanges (if any bindings have changed)
  • ngOnInit (after the first check only)
  • ngOnDestroy (at the very end before destruction) Implement this interface to get notified when any data-bound property of your directive changes
  • ngDoCheck
  • ngAfterContentInit
  • ngAfterContentChecked
  • ngAfterViewInit
  • ngAfterViewChecked

Official docs


Directive

Directives allow you to attach behaviour to elements in the DOM. It is also what you used to call a directive in AngularJS, but without a proper view. You can therefore place as many directives as you want on one DOM-element. This is not possible with components.

Official docs

Let’s get back to our previous component and this time, let’s make our presidential candidates red. To do so, we are going to create the redify directive:

import {Directive, ElementRef, Renderer} from 'angular2/angular2';

@Directive({
  selector: '[redify]'
})
export class Redify {
  constructor(private _element: ElementRef, private renderer: Renderer) {
      renderer.setElementStyle(_element, 'color', 'red');
  }
}

Notice that in order to obtain a reference to our Presidential Candidate element we injected _element: ElementRef.

Official docs for ElementRef

To modify the element style we injected renderer: Renderer, which is a service that gives you methods to manipulate the style of a particular element.

Official docs for Renderer

Then we can add the redify directive to our component:

import {Redify} from 'path/to/your/Redify/directive';

@Component({
    selector: "navbar",
    directives: [NgFor, Redify], 
    ...
    template: `
        <li redify *ngFor="#item of items; #i = index">{{item}} {{i}}</li>
    `
})

Result

Screen Shot 2015-12-12 at 19.28.28

http://embed.plnkr.co/iJiZVqixM0qAo4RMelB7/preview


Pipe

A pipe in Angular2 is the equivalent of filters in AngularJS. As in AngularJS, pipes can be stateless (pure functions, not reevaluated) or stateful (has dependencies that can modify the output).

A better explanation of what is a pipe is available in the previous article of this series of posts: Angular2 series – Template Syntax

Official docs

Let’s get back again to our previous component and this time, let’s create a pipe to transform our presidential candidates last name to uppercase.

First we create lastnameUppercase pipe:

import {Pipe} from 'angular2/angular2';

@Pipe({
  name: 'lastnameUppercase'
})
export class LastnameUppercase {
  transform(v, args) {
    return `${v.split(' ')[0]} ${v.split(' ')[1].toUpperCase()}`;
  }
}

Then let’s add this pipe to our navbar component in order to consume it.

import {LastnameUppercase} from './pipes';
@Component({
    selector: "navbar",
    ...
    pipes: [LastnameUppercase],
    template: `
        <li redify *ngFor="#item of items; #i = index">{{item | lastnameUppercase}} {{i}}</li>
    `
})

Demo

Screen Shot 2015-12-12 at 19.37.13

http://embed.plnkr.co/L1ERY1Pn6qmGl0B1hi0K/preview

Built in pipes

In Angular2 you have access to the following pipes for free:

  • currency
  • date
  • uppercase
  • json
  • limitTo
  • lowercase
  • async
  • decimal
  • percent

Service

Now that we saw how to create a component, a directive and a pipe, we are going to clean up our code and separate the data retrieval (the presidential candidates) into a service.

Official docs

import {Injectable} from 'angular2/angular2';

@Injectable()
export class PresidentialCandidate {

    constructor() {}

    getRepublicainList() {
        return [
        "Donald Trump",
        "Rand Paul",
        "Ben Carson"
      ]
    }

    getDemocraticList() {
        return [
        "Hillary Clinton",
        "Martin O'Malley",
        "Bernie Sanders"
      ]
    }
}

Now let’s consume this service on our navbar component:

import {PresidentialCandidate} from './services';

@Component({
    selector: "navbar",
    providers: [PresidentialCandidate],
    ...
    template: `
        <h2>Democratic Party presidential candidates</h2>
        <ul>
        <li redify *ngFor="#item of democrats; #i = index">{{item | lastnameUppercase}} {{i}}</li>
        </ul>
        <h2>Republican Party presidential candidates</h2>
        <ul>
        <li redify *ngFor="#item of republicans; #i = index">{{item | lastnameUppercase}} {{i}}</li>
        </ul>
    `
})
export class Navbar {
    democrats: Array<String>
    republicans: Array<String>

    constructor(private presidentialService :PresidentialCandidate) {
      this.democrats = presidentialService.getDemocraticList(); 
      this.republicans = presidentialService.getRepublicainList();
    }
}

We have decoupled the presidential candidates retrieval with the component that displays them. It is now easier for other components to consume this data.

Demo

Screen Shot 2015-12-12 at 19.40.57

http://embed.plnkr.co/z1B96b4OX7BHwkT0492G/preview


Application

AngularJS

With AngularJS an application was a simple module. It had no difference from any other module of your application.

angular.module('yourApp', []);

Angular2

With Angular2, it is similar. An application is a simple component as any other component of your application. It is just the root component that basically contains the scaffolding of your page.

@Component({
    selector: "yourApp"
})
@View({
    directives: [Header, Navbar, Content, Footer]
    template: `
      <header></header>
      <navbar></navbar>
      <content></content>
      <footer></footer>
    `
})
export class App {
  constructor() {
   
  }
}

Header, Navbar, Content and Footer are custom components, they do not exist within Angular2 core.

Bootstrap

Now that you know how to create components and a root component (or app), you need to bootstrap the application.

AngularJS

In AngularJS you could use angular.bootstrap(document, ['yourApp']); or the ng-app directive <body ng-app="yourApp">.

Angular2

In Angular2 it is very similar.

import {bootstrap} from 'angular2/angular2';
import {yourApp} from 'path/to/your/app/component';

bootstrap(yourApp, []);

Our application is now ready to be rendered. Insert your app component in your index.html file and reload the browser, your app is ready!

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

Thanks for reading, you can interact about this post by leaving a comment here, or directly on Twitter and Facebook!

Leave a Reply

Your email address will not be published. Required fields are marked *