AngularJS 2.0 changements notables

Publié le 16 juin 2015 par Numa Claudel | front

Cet article est publié sous licence CC BY-NC-SA

La version 2.0 d’Angular a maintenant été annoncée depuis un moment comme une refonte complète, et en voyant les importants changements entre les 2 versions, on peut dire que nous avons un gros travail de mise à niveau à fournir. Alors autant prendre un peu d’avance !

Cette version 2.0 étant en développement actif, elle change donc beaucoup, mais les développeurs ont fait quelques choix d’orientations que nous pouvons commencer à étudier. Pour ce faire, nous pouvons consulter le site dédié à Angular 2.0, sur lequel on trouve la documentation et quelques exemples et articles.

Angular 2.0 et TypeScript

Première chose à noter, Google et Microsoft collaborent pour écrire cette version Angular 2.0 avec le langage TypeScript.

TypeScript est un langage typé créé par Microsoft qui est un sur-ensemble de ES6. Il apporte entre-autres les annotations, l’introspection et le typage au JavaScript. Les annotations vont permettre de définir la configuration, les options des composants, tandis que l’introspection ajoute une vérification des types à l’exécution. Voici aussi une liste des types gérés par TypeScript.

Parmis ces quelques exemples qui nous sont proposés, voici celui du “Hello World”:

class Greeter {
  constructor(public greeting: string) { }

  greet() {
    return '<h1>' + this.greeting + '</h1>';
  }
};

var greeter = new Greeter('Hello, world!');
var str = greeter.greet();

document.body.innerHTML = str;

Ce code défini une classe JavaScript, avec un constructeur qui attend un attribut de type string. Le mot clef public défini devant l’argument permet de définir la portée de l’attribut, qui peuvent aussi être définis comme private. Utiliser un de ces mots clés devant un paramètre, comme dans cet exemple, permet de définir et d’initialier automatiquement une propriété du même nom. Cette classe comprend aussi une méthode greet qui retourne une balise h1 avec la valeur de la propriété greeting. La suite du code instancie donc la classe avec le fameux “Hello, world!” et appelle la méthode greet, avant de placer le résultat dans le document HTML.

Il est aussi possible de faire des héritages de classes avec le mot clé extends, des mixins avec le mot clef implements, et de définir des interfaces:

class Animal {
  name: string;
}

interface Caracteristic {
  height: number;
}

class Dog extends Animal implements Caracteristic { }

Mais je m’éloigne un peu du sujet, je vous laisse donc découvrir la suite.

TypeScript n’est toutefois pas obligatoire, les composants Angular 2.0 pourront aussi être écrits en Dart, ES5 ou ES6.

Quelques pertes

$scope s’en va

Le super $scope qui sert dans la version 1 de liant entre les vues et les contrôleurs est retiré, les propriétés sont définies dans les classes:

class Dog {
  name: string;

  constructor() {
    this.name = 'Snoopy';
  }
}

La variable name sera accessible dans la vue du composant comme suit:

<p>AngularJS 2.0 changements notables</p>

Fini le data-binding bi-directionnel

La directive ng-model qui permet d’avoir une mise à jour des données par l’utilisateur dans la vue, et dans le même temps coté contrôleur est retirée. La mise à jour du modèle par la vue devra passer par des évènements, et pour définir une variable locale au template, il faudra faire précéder un attribut du même nom de #:

<input #name (keyup) />
<input #email (keyup)="typing($event)" />
<p></p>
<p></p>

Vous pouvez voir qu’un évènement se défini avec son mot clé entre () et peut appeler une méthode du composant, dans ce cas typing, qui pourrait ajouter du traitement en plus de modifier la valeur de email. D’autre part la valeur d’une variable locale au template est accessible par un attribut value sur celle-ci.

Bye bye jqLite

angular.element et son mini jQuery embarqué par la première version d’Angular s’en va aussi. Si nous avons besoin de jQuery, alors il faudra le charger.

Vers les components

Le MVW pattern est remplacé par le concept des components, pour lesquels les contrôleurs seront maintenant réservés.

Un composant comprend donc une classe JavaScript qui est le contrôleur, il se concentre sur la logique propre au composant, et des annotations qui définissent sa configuration. On retrouve par exemple la définition du sélecteur, du template et des dépendances.

Un exemple d’annotations:

@Component({
  selector: 'greeting'
})
@View({
  template: `
    <h2>
      Hello 
      <span *if="!name">World</span>
      !
    </h2>
  `,
  directives: [If]
})

Ces annotations définissent un sélecteur qui va chercher à s’accrocher sur une balise <greeting></greeting>, une dépendance à la directive If, et un template qui utilise une variable name et la directive If. Au passage vous remarquez que les directives fournies par le framework sont préfixées par *. On aurait d’ailleurs aussi pu l’utiliser avec la forme *ng-if.

Dans cet exemple name serait une valeur définie dans le contrôleur, et non une valeur saisie par l’utilisateur, auquel cas il faudrait la définir dans le template comme suit: .

Vous remarquez aussi qu’il est possible de définir une chaine de caractère sur plusieurs lignes, ce qui évite les enchainements de 'string' + variable + 'string' +\n autre ligne + .... J’en profite pour relever que ES6 nous apporte l’interpolation dans les chaines de caractères:

console.log(`Log: ${value}`);

Ça c’est top !

Les formulaires

Un nouveau module Forms fait son apparition pour améliorer la gestion des formulaires, et pour compenser la perte de ng-model. Il contient une classe FormBuilder qui permet de définir programmatiquement les options d’un formulaire. Parmi les options disponibles pour la construction du formulaire, nous allons par exemple pouvoir définir des contraintes de validations.

Disons que nous avons une classe User comme suit:

class User {
  name: string;
  email: string;
  age: number;
}

Construction un composant de type formulaire pour cette classe:

@Component({
  selector: 'user-form',
  injectables: [FormBuilder]
})
@View({
  templateUrl: 'user_form.html',
  directives: [formDirectives]
})

class UserForm {
  user: User;
  form: ControlGroup;

  constructor(builder: FormBuilder) {
    this.user = new User();

    this.form = builder.group({
      name: [(this.user.name || ''), Validators.required],
      email: [(this.user.email || ''), Validators.required],
      age: [(this.user.age || null)]
    });
  }

  submit() {
    for(var key in this.form.controls) {
      this.user[key] = this.form.controls[key].value;
    }

    console.log(this.user);
    return false;
  }
}

bootstrap(UserForm);

user_form.html:

<form [control-group]="form">
  Nom : <input control="name">
  <div *if="!form.controls.name.valid">Champ requis</div>
  Mail : <input control="email">
  <div *if="!form.controls.email.valid">Champ requis</div>
  Age : <input control="age" size="3">
  <button (click)=submit()>Valider</button>
</form>

Je me suis basé sur la documentation actuelle sur les formulaires, ainsi que quelques articles dont celui-ci pour construire cet exemple. Ce n’est pas encore très fonctionnel, mais ça permet de se faire une idée.

FormBuilder sert donc à construire/définir les options du formulaire au sein de la classe du composant. On le passe à la classe UserForm à l’instanciation avec l’option injectables dans l’annotation Component. formDirectives est passé à l’annotation View avec l’option directives, et semble être un set de toutes les directives du module Forms nécéssaire à la construction d’un formulaire. ControlGroup sert à contenir un ensemble de Control, ici c’est la variable form, et on retrouve sa directive associée dans le template définie avec [control-group]="attribut". Les directives suivantes sont donc des Control définies avec control="attribut" dans le template. C’est ici un raccourci de syntaxe, la syntaxe complète est la suivante:

<input [control]="form.controls.name">

Un attribut défini entre [] signifie “binding”. Enfin nous avons la classe Validators qui nous sert à définir les contraintes de validation comme son nom l’indique.

Ancien VS futur Hello World

Pour comparer, reprenons le Hello World de Introduction à AngularJS et construisons le en Angular 2.0:

index.html:

<!doctype html>
<html>
  <head>
    <title>Hello World en Angular 2.0</title>
    <script src="https://github.jspm.io/jmcriffey/bower-traceur-runtime@0.0.87/traceur-runtime.js"></script>
    <script src="https://jspm.io/system@0.16.js"></script>
    <script src="https://code.angularjs.org/2.0.0-alpha.23/angular2.dev.js"></script>
    <meta charset="utf-8">
  </head>
  <body>
    <h1>Hello World en Angular 2.0</h1>
    <hr />

    <hello></hello>

    <script>
      System.import('hello');
    </script>
  </body>
</html>

hello.html:

<label for="name">Nom :</label>
<input type="text" id="name" #name (keyup) />
<button (click)="log(name.value)">Log</button>
<br />
<h2>
  Hello 
  <span *if="!name.value">World</span>
  !
</h2>

hello.ts:

/// <reference path="typings/angular2/angular2.d.ts" />

import { Component, View, bootstrap, If } from 'angular2/angular2';

// Annotation section
@Component({
  selector: 'hello'
})
@View({
  templateUrl: 'hello.html',
  directives: [If]
})

// Component controller
class HelloComponent {

  constructor() { }

  log(value) {
    console.log(`Log: ${value}`);
  }
}

bootstrap(HelloComponent);

En début du fichier hello.ts on défini une source de référence, à partir de laquelle nous pouvons importer les modules dont le composant a besoin. Pour référencer ce fichier j’ai suivi le QuickStart qui nous fait installer les sources d’Angular 2. Il faudra préalablement compiler le TypeScript en JavaScript pour que le code soit interprété par le navigateur, donc dans ce cas j’ai utilisé la commande indiqué en remplaçant app.ts par hello.ts.

Conclusion

Il y a d’autres points que j’aurais pu aborder, mais je voulais surtout vous faire un retour de ce qui ressort en premier et de ce qui permet de faire le lien avec la première version. Il est vrai que ce “nouveau” framework demande un bel effort de mise à niveau, mais j’ai tout de même hâte de voir ce que va donner la première release de la 2.0 ;).

L’équipe Synbioz.

Libres d’être ensemble.