:::: MENU ::::

Préparer la migration de AngularJS 1.x vers AngularJS 2.0

AngularJs 2 est désormais disponible en version beta mais de nombreuses applications débutent encore sur la version 1 qui sera toujours supportée jusqu’en 2018. Dans la premiere partie nous avons que vu les principaux concepts utilisés par Angular 1.x sont amener à évoluer voir disparaître. Or il est possible dès à présent de se préparer à ces changements et anticiper une migration en mettant en place point par point des techniques issues de bonnes pratiques et de nouvelles fonctionnalités.

La fin des contrôleurs

Eviter l’usage de $scope et utiliser la syntaxe ControllerAs

La fonctionnalité controllerAs introduite depuis la version 1.2 permet une flexibilité non négligeable sur l’utilisation des contrôleurs en le dissociant du $scope . Ainsi le contrôleur devient une simple fonction et le $scope un composant injectable qui indique que l’on utilise les fonctionnalités d’Angular.

On peut utiliser controllerAs à chaque fois que l’on appel un contrôleur :

Dans une vue :




<div ng-controller="myController as vm">
 <input type="text" ng-model="vm.value">
 <button ng-click="vm.myFunction()"></button>
</div>



Sur une directive


app.directive("myDirective",function() {
  return {
    template :"<Hello {{vm.world}}></div>",
    controller :function(){
      this.world = "World";
    },
    controllerAs :"vm"
 }
});

Avec ui-router


$stateProvider
.state("connexion", {
  url: "/connexion",
  templateUrl: "views/login.html",
  controller: function() {
    this.user= {
      login : "user",
      pass : "123"
    };
  },
 controllerAs: "vm"
 });

De cette manière, les données que l’on veut exposer à la vue ne sont plus liées au $scope. Par contre si l’on souhaite utiliser les fonctionnalité de $watch ou des événements propres à Angularjs , alors il faudra quand même injecter le $scope.


function myController($scope, myService) {
  this.myModel = myService.getData() ;

  $scope.$on("myEvent",function(){
   // TODO
   });
}

Mais maintenant la dépendance est clairement maîtrisée et dans le cas ci dessus, il sera aisé de remplacer le système de gestion d’événement .

supprimer le $scope, c’est supprimer $watch $apply $timeout .. des notions qui compliquent l’apprentissage du framework et sont souvent à l’origine de hacks

La notion de composant

La tendance en matière de développement ces derniers mois tend vers le composant et le succès de React en témoigne. Les web-components vont également dans ce sens.

Si AngularJs 2 intègre lui aussi ce concept, il est possible de l’utiliser avec Angularjs 1.x.

Tout est directive

Dès que vous souhaitez créer un élément d’interface, au lieu de créer un controleur et d’utiliser ng-controller, préférez l’usage d’une directive. Mais attention, une directive qui utilise un contrôleur avec les notions vues précédemment.

Plutot que d’avoir dans un template :


<div ng-controller="TodoCtrl">

  <input ng-model="input" />
  <button ng-click="add()">Add</button>

  <ul>
    <li ng-repeat="item in myTodoList">...</li>
  </ul>


</div>

puis dans le code


app.controller("TodoCtrl",function($scope) {
  $scope.myTodoList = [];
  $scope.add = function() {
    $scope.myTodoList.push($scope.input);
  }
});

Faites une directive avec un contrôleur , en appliquant la syntaxe controllerAs :

app.directive("todo",function() {
 return {
   scope: {},
   controllerAs :"vm",
   controller :function TodoCtrl() {
     this.myTodoList = [];
     this.add = () => { // arrow function d'ES6 qui permet de transmettre le this facilement
       this.myTodoList.push(this.input);
    }
   },
   templateUrl :"todoTemplate.html",
   bindToController: true
 };
});

Angular.component

La version 1.5 d’angular apporte un nouveau type de fonction appelé component. Il s’agit d’un wrapper permettant de simplifier l’écriture des directives de type composant et de diminuer la verbosité de ce qu’on appel le DDO . L’avantage est double puisque ce gain en lisibilité encourage l’utilisation des directives « composant » et c’est aussi la logique employée pour Angular 2 .

Voila notre directive précédente réécrite avec angular.component :


app.component("todo",{
  controller : function TodoCtrl() {
    this.myTodoList = [];
    this.add = () => { 
    this.myTodoList.push(this.input);
  },
  templateUrl :"todoTemplate.html",
})

Quelques précisions :

  • Dans le template , la référence au controlleur sera par défaut le nom du composant, ici « todo ». Si vous souhaitez utiliser un nom spécifique, vous pouvez utiliser l’argument controllerAs comme précédemment .
  • Par défaut, la fonction composant créé une directive avec un scope isolé. Si vous souhaitez effectuer des bindings il faut utiliser l’option…bindings qui fonctionne comme le paramètre scope des directives actuelles.

En attendant une doc officielle après la sortie de la version 1.5 finale, je vous conseille de lire cet article qui explique en détail l’utilisation de angular.component

Modules, dépendances et découplage

La plupart des fonctionnalités d’Angular sont enfait de simples fonctions . Prenons ici encore notre exemple avec la directive.  Une bonne pratique consiste à découpler l’initialisation du service de son implémentation.


app.directive("todo",todoDirective);

function todoDirective() { 
  return {
   scope: {},
   controllerAs: "vm",
   controller: function TodoCtrl() {
     this.myTodoList = [];
     this.add = () => { // arrow function d'ES6 qui permet de transmettre le this facilement
       this.myTodoList.push(this.input);
     }
   },
   templateUrl: "todoTemplate.html",
   bindToController: true
 };
});
} 

De cette manière il facile d’exporter le contenu de todoDirective dans un module indépendant et de le charger avec un require() commonjs et un import ES6.
En découplant ainsi on augmente également la testabilité de ses composants.

Tirer profit des fonctionnalités d’ECMAscript 6/2015

Angular 2 est écrit en TypeScript et sans rentrer dans les détails , si on met de coté la notion de typage on est très proche de la syntaxe et des fonctionnalités d’ Ecmascript 2015 .
Pas de panique si vous ne voulez pas passer par un transpileur, vous pouvez toujours utiliser Ecmascript 5 mais je vous conseille très fortement de vous penchez au moins sur Ecmascript 6/2015 puisqu’il est officiellement le standard javascript actuel . Il apporte de nombreuses améliorations et vous simplifiera la migration vers Angular 2 .

Les fonctionnalités d’Ecmascript 6 à utiliser en priorité pour faciliter une future migration et des évolutions sont :

Services

Angular 1 propose différentes méthodes pour faire la même chose (provider, factory, service, value, constant) .  .

Dans Angular 2, les services sont de simples classes et le fonctionnement actuel de service est assez similaire. Comme il est relativement simple de transformer une fonction de type service en classe, il est préférable de n’utiliser que angular.service dans Angular 1. Pour faciliter la migration, les services peuvent être des classes.

Préférez l’usage de angular.service .

En combinant ce qu’on vient de voir on peut arriver à ça :

//Fichier mon-service.js
export default class MonService {
  constructor($http) { // injection du service $http d'angular
    this.$http = $http;
  }
  getTodos() {
    return this.$http.get("/todos")
  }
}

//Fichier mon-controleur.js
export default class MonControleur {
  constructor(MonService) {
    this.myTodoList = [];
    MonService.getTodos()
      .then((result) => this.myTodoList = result.todos);
  }

  addTodo() {
    this.myTodoList.push(this.input);
  }
}

//Fichier todocomponent.js
import MonControleur from "./mon-controleur"

const TodoComponent = {
  controller :MonControleur ,
  templateUrl :"todoTemplate.html" // on pourrait aussi inliner le template avec 
                                   // webpack ou l'interpolation de chaines en ES6
};

export default TodoComponent;

//Fichier index.js
//
import angular from "angular"
import TodoComponent from "./todocomponent"
import MonService from "./mon-service.js"

angular.module("app",[])
       .service("MonService",MonService)
       .component("monComposant",TodoComponent)

Utiliser les outils

ngUpgrade et ngForward

L’équipe derrière Angular 2 propose deux outils pour faciliter la migration vers Angular2.

ngUpgrade permet de faire cohabiter angular 1 et angular 2 au sein de la même application. L’idée est de migrer petit à petit les composants et les services sans avoir à tout réécrire d’un coup.

ngForward quant à lui sera utilisé uniquement dans des applications sous angular 1 et permettra d’utiliser la syntaxe d’angular 2.

Si vous souhaitez en savoir plus, je vous recommande cet article expliquant le processus de migration vers Angular 2 en utilisant ngUpgrade

Le nouveau router d’Angular

Depuis la version 1.4 d’Angular, un nouveau routeur est disponible. Son avantage est qu’il est compatible entre les deux versions. Vous pouvez commencer à l’utiliser pour vos projet en Angular 1, la migration sera alors largement simplifiée. Néanmoins si vous suivez les conseils précédents avec un autre routeur, la logique est souvent la même.

Plus le couplage est faible, plus la migration sera facilité

Angular 1.5 +

Si la dernière version 1.4 d’angular s’est plutôt concentrée sur des corrections, la prochaine 1.5 sera un vecteur de nouveautés notamment pour faciliter la migration. C’est le cas pour angular.component qu’on à pu voir plus haut mais l’équipe travail aussi sur les modules animations et traduction. L’objectif est d’apporté un maximum de compatibilité entre les deux versions.

angular_superhero

Conclusion

Résumer le meilleur moyen d’écrire des application avec Angular 1 pour préparer la migration est simple : faites du javascript. C’est d’autant plus vrai depuis Ecmascript 6, n’utilisez le framework que là où c’est nécessaire, gardez le plus possible de code natif et découplé. Vous gagnerez sur tous les tableaux.

Le simple fait de réfléchir à comment mettre en place les stratégies évoquées vous fera penser à votre architecture et la manière la plus simple d’y parvenir

Enfin, de nombreux développeurs travaillent pour faciliter la migration entre les deux version afin qu’elle soit le moins complexe possible malgré les nombreux changements d’architecture et d’API. N’hésitez pas à passer dès que possible et petit à petit sur ces outils si vous envisagez une migration future.

Notes:
1. $on / $emit
2. et même conseillé puisque travailler de cette manière oblige à penser l’isolation et la réutilisation éventuelle de son code
3. actuellement encore en beta
4. ce qui est normal puisque ES2015 est un sous ensemble de typescript
5. vers la version 2 ou un autre framework ou ..pas de framework..
$on / $emit
et même conseillé puisque travailler de cette manière oblige à penser l’isolation et la réutilisation éventuelle de son code
actuellement encore en beta
ce qui est normal puisque ES2015 est un sous ensemble de typescript
vers la version 2 ou un autre framework ou ..pas de framework..

8 Comments

    • Répondre maxdow |

      Merci. On commence à trouver de plus en plus de retour. Il me semble même avoir vu que l’équipe d’angular parlait de la possibilité d’utiliser déjà Angular 2 en prod

  • Répondre nkos |

    Très intéressant merci pour cet article !!
    mais une question demeure : Javascript ou TS ? merci

    • Répondre maxdow |

      Merci :) TS se rapproche sur beaucoup d’aspects de ES2015/ES6. Travailler avec ES6 est un énorme plus mais il est encore indispensable d’utiliser un outillage pour assurer la compatibilité de son application. Comme TS nécessite le même genre d’outil ( babel par ex ) il est facile de l’utiliser.

      Mon avis perso, je préfère rester sur du standard donc javascript. Après TS est poussé par des gros ( microsoft ) , apporte la notion de typage très cher à certains et n’a pas une syntaxe trop éloignée du js ( comme peut l’être coffescript par ex ) . Alors pourquoi pas, au moins pour tester.

So, what do you think ?