Devoxx 2016 : Retour sur « D’Angular 1 à Angular 2 : Préparez vous dès maintenant à la migration »

devoxx_france_2016

J’ai eu la chance de participer cette année à la 5ème édition du Devoxx France. Je vous propose dans cet article de revenir sur une des conférences que j’ai particulièrement appréciée dont le thème était "D'Angular 1 à Angular 2 : Préparez vous dès maintenant à la migration" présentée par Benoit Lemoine.

L’annonce de la sortie d’Angular 2 a été faite en septembre 2014 et cette version 2 n’a pas grand chose à voir avec Angular 1. Cependant, aucun chemin de migration n’a été prévu et cela va provoqué ce qui a été appelé le Angular Bashing ou un rejet d'Angular en 2015. À partir de ce constat, un chemin de migration va être mis en place.

Les grandes différences entre Angular 1 et Angular 2 sont les suivantes :

  • Javascript -> Typescript
  • Orienté Modèle-Vue-* -> Orienté composant
  • Double databinding -> Databinding presque unidirectionnel

Les étapes de migration que présente Benoit Lemoine nécessaires pour gérer ces différences sont les suivantes :

Etape n°1 : Suivre les bonnes pratiques

Tout d'abord, il faut organiser son code en modules fonctionnels et non techniques. Il est donc préconisé de regrouper le service, le controller, le template HTML et le fichier CSS nécessaires pour un module fonctionnel et de ne pas regrouper ensemble tous les controllers, tous les services, etc...

Ensuite, il est préférable d'utiliser les services d'Angular et d'éviter les factory. Au niveau des controllers, il est conseillé de bannir l'utilisation de $scope et d'utiliser controllerAs au niveau du router.

Etape n°2 : Les composants

Un des grands changements cité ci-dessus est le passage d’un modèle orienté Modèle-Vue-* vers un modèle orienté composant. Ce modèle, très répandu côté JavaScript avec l’arrivée de ReactJS, a été choisi pour Angular 2. Pour faire simple, dans ce modèle, tout est composant : De l’input au formulaire, en passant par une page et l’application en elle-même, chaque élément est un composant. L’application peut donc être représentée sous la forme d’un arbre de composants dont la racine serait l’application elle-même.
À partir de cette observation, un nouveau routeur orienté composant a été développé afin de permettre le routage entre les différentes briques de l’application.

Etape n°3 : Le système de modules dans Angular 1

Angular 2 et Typescript utilisent la notion de modules pour l’isolation du code de chaque composant. Les mots-clés import et export permettent l’utilisation des composants dans d’autres en gardant des composants cloisonnés. Plusieurs systèmes de modules sont supportés tels que AMD, UMD, CommonJS, ES6 ou encore SystemJS. C’est ce dernier qui est utilisé par défaut dans Angular 2.
En matière de package manager, Benoit Lemoine a abordé le dernier d’entre eux : JSPM. Sa force consiste à charger n’importe quel type de modules cités précédemment et depuis n’importe quel repository comme npm, github, bitbucket, etc… De plus, il a une intégration forte avec SystemJS.
Un autre grand changement d’Angular 2 est l’utilisation de Typescript. Ce langage créé par Microsoft en 2012, open-source et qui transpile vers Javascript est un sur-ensemble d’EcmaScript 2015 ou EcmaScript 6. Il est statiquement typé avec un typage structurel et graduel.

En voici un exemple :

interface User {
  firstName:string
  lastName:string
}

class Room {
  constructor(private users:Array<User> = []) {}

  addUser(user:User) {
    this.users.push(user);
  }
}

const room = new Room();
room.addUser({firstName:'Georges', lastName:'Abitbol'});

Typescript est décrit comme étant simple d’apprentissage et facile à migrer grâce à une complétion dans les IDE, un refactoring simplifié et des types qui servent aussi de documentation.

Typescript supporte aussi des librairies tierces par des fichiers d’en-tête (*.d.ts) grâce à un autre package manager qui est Typings.

En ce qui concerne le typage, plusieurs bonnes pratiques sont à suivre. Il est important de ne plus utiliser de var mais des let ou const lors de la définition de variables. L’usage du const est conseillé si on veut avoir une application avec un maximum d’immutabilité. Il existe le mot-clé any mais celui-ci est à bannir. Il faut maintenant typer explicitement les entrées et les sorties et éviter les appels dynamiques.
Une autre différence entre Angular 1 et Angular 2 correspond à l’utilisation de $watch. Celle-ci n’est plus possible dans Angular 2, Benoit Lemoine nous montre donc sa solution à l’aide de getter et setter :

class TurtlesCtrl {
  public turtles:Array<Turtle> = []

  get firstTurtleName():string {
    return this.turtles[0] && this.turtles[0].name;
  }
  set firstTurtleName(firstTurtleName:string) {
    if (!this.turtles[0]) {
      this.turtles[0] = {name:'', color:''}
    }
    this.turtles[0].name = firstTurtleName;
  }
}

L’utilisation de décorateurs sur les méthodes est aussi une nouveauté. Ils permettent d’ajouter du comportement transverse aux méthodes à l’aide d’annotations telles que throttle, inject ou encore memoize. Ce dernier permet par exemple de renvoyer une fonction d’une valeur identique qu’au précédent appel dès lors que cette fonction a été appelée avec les mêmes arguments. Cela engendre un gain de performances à l’aide d’une seule annotation @memoize sur la fonction.

Etape n°4 : Angular 2 dans Angular 1

Pour commencer, il faut installer Angular 2 à l’aide du package manager choisi. Pour que votre application fonctionne, il faut récupérer au même moment rxjs et reflect-metadata.

Pour utiliser Angular 2 dans une application Angular 1, peu importe l’ordre et la position des modules Angular 2 dans l’arbre définissant l’application hybride, celle-ci fonctionnera correctement tant que la racine restera un module Angular 1.

Ensuite, pour ugrader des composants et services Angular 1 ou de downgrader des composants et services Angular 2, nous devons utiliser UpgradeAdapter.
Dans le premier cas, nous souhaitons intégrer une directive Angular 1 dans un composant Angular 2. Voici comment l’utiliser :

const MyAngular1Directive =
   upgradeAdapter.upgradeNg1Component('myAngular1Directive');

@Component({
  selector:'MyAngular2Directive',
  template:`
  <div>
    <My-Angular1-Directive my-params="2" />
  </div>`,
  directives:[MyAngular1Directive]
})
export default class MyAngular2Directive {

};

Dans le second cas, on souhaite utiliser un composant Angular 2 dans notre application Angular 1. Pour cela, la méthode de downgrade de l'UpgradeAdapter va permettre de créer une directive Angular 1 à partir du composant Angular 2.

import angular from 'angular';
import upgradeAdapter from 'upgrader';
angular.module('turtles.app').directive('turtlesShow',
        <IDirectiveFactory>upgradeAdapter.downgradeNg2Component(turtlesShow2))

Une autre solution qui s’appelle Ng-forward consiste à écrire du code avec les conventions Angular 2 avec du Angular 1.3+. Cette solution peut être une première étape avant d’écrire du vrai code Angular 2.

En conclusion, pour réaliser une bonne migration d’une application Angular 1 vers Angular 2, il faut suivre les bonnes pratiques Angular, utiliser un système de modules avec Typescript, migrer les « feuilles » de votre arbre de composants en finissant par le routage et le composant le plus haut. 



 

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Captcha *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.