Angular 4 : quelques nouveautés

Angular 4 est sorti en mars conformément aux prévisions de Google et apporte des nouveautés intéressantes pour les développeurs.

On passe donc d’Angular 2 à Angular 4 mais pourquoi ? Le but est que toutes les bibliothèques Angular soient hébergées sur un seul dépôt GitHub avec le même numéro de version. Mais bien qu’aujourd’hui elles soient toutes en version 2.3.0, @angular/router est en version 3.3.0. Ce qui explique le passage en version 4. De plus, Angular a décidé dorénavant d’utiliser le principe de SemVer (Semantic Versioning) pour ses futures versions.

Je vais vous présenter dans cet article quelques nouveautés qui ont retenu mon attention. Cette liste est bien entendu non exhaustive. Le but de cet article est de montrer des exemples de code simples illustrant les nouvelles caractéristiques du langage. Toutefois une connaissance minimale d’Angular 2 est nécessaire pour bien comprendre les exemples présentés.

Pour tester ces nouveautés, nous allons commencer par créer un projet Angular avec l’outil angular-cli. On l’installe via NPM de cette manière :

npm install -g @angular/cli

Par défaut angular-cli va utiliser NPM pour gérer les dépendances. On peut modifier cette configuration pour plutôt utiliser Yarn. Yarn est plus performant et s’installe très rapidement (voir les articles installer yarn et comparatif Yarn vs NPM). Pour cela, on exécute la commande :

ng set --global packageManager=yarn

On peut ensuite créer notre projet :

ng new angular-features

Puis lancer un serveur de développement en prenant soin de se placer dans le projet créé :

ng serve

Ensuite, j’ai décidé de créer un composant par feature. On crée facilement un composant de cette manière :

ng g c component1 # équivalent à : ng generate component component1

Les composants que je vais vous présenter étant simples se divisent en un fichier TypeScript qui définit le composant et un fichier HTML pour son rendu.

Passons maintenant aux exemples.

Exemple 1 : *ngIf et ng-template

Angular 4 enrichit la syntaxe de la directive *ngIf. On peut désormais ajouter un else. La syntaxe est la suivante :

<div *ngIf="condition1; else condition2">
    condition1 est vraie
</div>
<ng-template #condition2>
   condition2 est vraie
</ng-template>

Nous voyons aussi que la syntaxe du ng-template a évolué. Voici un exemple concret :

component1.component.ts
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-component1',
  templateUrl: './component1.component.html'
})
export class Component1Component implements OnInit {

  authenticated: boolean;

  ngOnInit() {
    this.authenticated = false;
  }

  login() {
    this.authenticated = true;
  }

  logout() {
    this.authenticated = false;
  }

}
component1.component.html
<div *ngIf="authenticated; else notAuthenticated">
    The user is logged in.
    <button class="btn btn-primary" (click)="logout()">Log out</button>
</div>
<ng-template #notAuthenticated>
    Please login to continue.
    <button class="btn btn-primary" (click)="login()">Log in</button>
</ng-template>

Exemple 2 : *ngIf enrichi

On peut gérer de manière plus fine encore la directive *ngIf avec une syntaxe de type if … then … else … Voici la syntaxe :

<div *ngIf="condition; then execute1 else execute2"></div>
<ng-template #execute1>
    condition est vraie
</ng-template>
<ng-template #execute2>
    condition est fausse
</ng-template>

L’exemple est similaire au précédent.

component2.component.ts
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-component2',
  templateUrl: './component2.component.html'
})
export class Component2Component implements OnInit {

  authenticated: boolean;

  ngOnInit() {
    this.authenticated = false;
  }

  login() {
    this.authenticated = true;
  }

  logout() {
    this.authenticated = false;
  }

}
component2.component.html
<div *ngIf="authenticated; then isAuthenticated else notAuthenticated"></div>
<ng-template #isAuthenticated>
    The user is logged in.
    <button class="btn btn-primary" (click)="logout()">Log out</button>
</ng-template>
<ng-template #notAuthenticated>
    Please login to continue.
    <button class="btn btn-primary" (click)="login()">Log in</button>
</ng-template>

*Exemple 3 : ngIf et asynchronisme

Angular 4 permet aussi d’ajouter de l’asynchronisme directement dans le *ngIf, ce qui n’était pas possible avant. Voici la syntaxe :

<div *ngIf="someObservable | async; else loading; let myObject">
  myObject est prêt à être affiché
</div>
<ng-template #loading>Loading...</ng-template>

L’exemple suivant simule un appel vers un chargement de données d’une durée de deux secondes montrant ainsi que la condition évolue après les deux secondes.

component3.component.ts
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/delay';

@Component({
  selector: 'app-component3',
  templateUrl: './component3.component.html'
})
export class Component3Component implements OnInit {

  user: Observable<{}>;

  ngOnInit() {    
    this.user = this.getAsyncData();
  }
  
  getAsyncData() {
    return Observable.of({ firstName: 'Steve', lastName: 'Jobs' }).delay(2000);
  }

}
component3.component.html
<div *ngIf="user | async; let user; else loading"> {{user.firstName}} {{user.lastName}} </div> <ng-template #loading> Loading User Data ... </ng-template>

Exemple 4 : Renderer2

Un nouveau viewEngine fait son apparition : Renderer2. Il remplace Renderer (maintenant déprécié). Celui-ci présente l’avantage de générer beaucoup moins de code. Voici un exemple d’utilisation pour appliquer un style sur un élément :

component4.component.ts
import { Component, Renderer2 } from '@angular/core';

@Component({
  selector: 'app-component4',
  templateUrl: './component4.component.html'
})
export class Component4Component {

  constructor(private renderer: Renderer2) {}

  onChangeBackground(element: HTMLElement) {
    this.renderer.setStyle(element, 'background-color', 'red');
  }

}
component4.component.html
<div #myDiv>
    <button class="btn btn-primary" (click)="onChangeBackground(myDiv)">Change style</button>
</div>

Exemple 5 : Directive email

Angular 4 introduit une directive “email” permettant de valider automatiquement la bonne saisie d’un email dans un champ texte. La syntaxe est la suivante :

<input type=”email” email … />

L’exemple présenté en dessous montre comment utiliser cette directive sur un formulaire simple :

component5.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-component5',
  templateUrl: './component5.component.html'
})
export class Component5Component {}
component5.component.html
<form #f="ngForm">
    <input type="email" ngModel name="email" required email>
    <button class="btn btn-primary" [disabled]="!f.valid">Submit</button>
    <p>Form state : {{ f.valid ? 'VALID' : 'INVALID' }}</p>
</form>

Exemple 6 : Les animations

Les animations pour Angular 4 sont maintenant regroupées dans un module spécifique @angular/animations non importé par défaut par angular-cli. Ce changement a été rendu obligatoire pour permettre de maintenir la fonctionnalité de tree-shaking. On va l’ajouter à notre projet grâce à Yarn de cette façon :

yarn add @angular/animations@4.0.0

La façon d’utiliser les animations ne changent pas, on a juste un import spécifique pour les animations :

import { animate, state, style, transition, trigger } from '@angular/animations';

L’exemple d’animation présenté en dessous montre comment changer la couleur de fond d’une div via un bouton.

component6.component.ts
import { Component, OnInit } from '@angular/core';
import { animate, state, style, transition, trigger } from '@angular/animations';

@Component({
  selector: 'app-component6',
  templateUrl: './component6.component.html',
  animations: [
    trigger('updateBgColor', [
      state('blue', style({
        backgroundColor: 'blue'
      })),
      state('white', style({
        backgroundColor: 'white'
      })),
      transition('blue => white', animate(300)),
      transition('white => blue', animate(300))
    ])
  ]
})
export class Component6Component implements OnInit {

  color: any;

  ngOnInit() {
    this.color = 'white';
  }

  updateBgColor() {
    this.color = this.color === 'white' ? 'blue' : 'white'; 
  } 

}
component6.component.html
<div [@updateBgColor]="color">
    <button class="btn btn-primary" (click)="updateBgColor()">Update bg color</button>
</div>

Exemple 7 : Titlecase

Un nouveau filtre apparaît permettant d’appliquer des majuscules sur la première lettre de chaque mot pour une chaîne de caractères donnée.

component7.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-component7',
  templateUrl: './component7.component.html'
})
export class Component7Component {

  text: string = "hello world";

}
component7.component.html
 {{ text | titlecase}}
<!-- Affiche Hello World -->

Conclusion

On peut également noter que Angular 4 supporte désormais Typescript 2.1. Enfin Angular propose un guide pour migrer son application vers Angular 4 : Angular Update Guide. Angular 4 n’est donc pas une révolution par rapport au passage de Angular 1 à 2 mais une évolution apportant des nouveautés pratiques.

Si vous souhaitez approfondir avec d’autres caractéristiques apportées par Angular 4, je vous conseille ce lien : http://angularjs.blogspot.fr/2017/03/angular-400-now-available.html

Vous pouvez retrouver l’ensemble des exemples de code sur mon github.