Apprendre à programmer avec le framework Angular

Le Data Binding Angular

William Koza

Avec cette première approche du framework Angular, je vous propose d'introduire une capacité fondamentale du produit de Google : Le Data Binding. Ce tutoriel va donc nous apprendre les différentes techniques qui nous sont offertes pour réaliser du Data Binding.

Commentez

- Titre : Apprendre à programmer avec le framework Angular

- Auteur : William Koza

- Parution : 5 décembre 2016

- Mise à jour : 5 décembre 2016

- Public visé :

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2016 William Koza. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- I-A. {{L'interpolation}}

- II. [Le Property Binding]

- II-A. Le Property Binding sur un élément du DOM

- II-B. Le Property Binding sur un attribute directive

- II-C. Le Property Binding sur une propriété d'un component

- II-D. (L'Event Binding)

- II-E. [(Le two-way Data Binding)]

- II-E-1. [(Le two-way Data Binding avec NgModel)]

- III. Conclusion

- IV. Remerciements

I - Introduction

Le Data Binding est un élément essentiel dans les frameworks de Single Page Application. Il permet de synchroniser la vue au modèle JavaScript sous-jacent. Voici un schéma général du fonctionnement du Data Binding :

Angular 2 a défini quatre sortes de Data Binding pour synchroniser le template et le component. Il est ainsi possible de propager une donnée du component vers le DOM, du DOM vers le component et dans les deux sens. Ces formes de Data Binding sont communément nommées:

- Interpolation : ce mécanisme permet de modifier le DOM à partir du modèle, si un changement est intervenu sur une valeur de ce dernier.

- Property Binding : ce mécanisme permet de valoriser une propriété d'un composant ou d'une directive à partir du modèle, si un changement est intervenu sur une valeur de ce dernier.

- Event Binding : ce mécanisme permet d'exécuter une fonction portée par un component suite à un évènement émis par un élément du DOM.

- Le "two-way" Data Binding : c'est une combinaison du Property Binding et du Event Binding sous une unique annotation. Dans ce cas-là, le component se charge d'impacter le DOM en cas de changement du modèle et le DOM avertit le component d'un changement via l'émission d'un évènement. Le mécanisme se rapproche du fameux ngModel Angular 1, mais avec des algorithmes de Data Binding différents.

AngularJs a largement contribué à faire connaître ce mécanisme, c'était même l'un de ses principaux atouts mis en avant. Mais son mécanisme de Data Binding a également été critiqué pour ses "mauvaises" performances. Notamment le fameux "dirty checking" qui va vérifier l'ensemble des expressions pour voir s'il existe un changement pour être ensuite relancé pour gérer les watchers…

I-A - {{L'interpolation}}

Nous allons enfin commencer à dynamiser nos applications grâce au Data Binding ! Commençons par créer un nouveau projet prj-data-binding à l'aide d'Angular-Cli :

Si vous désirez plus d'informations à ce sujet, je vous conseille le tutoriel Démarrer avec Angular-Cli.

et je vous propose de modifier notre AppComponent comme ceci :

App.component.ts

1. import { Component } from '@angular/core';

3. @Component(

4. {

5. selector: 'app-root',

6. template: ` <div>Personne : {{person}} | Age : {{age}} | Adresse :

7. {{address}}</div> `

8. })

9. export class AppComponent {

11. person:string= 'John Doe';

12. age:number= 30;

13. address:any= {street:'rue du Paradis', city:'75010 Paris'};

15. }

Dans cet exemple, nous avons un template basique pour décrire l'identité d'une personne. J'ai utilisé les {{ }} pour binder les propriétés du component avec leur pendant dans le template. Avec ce mécanisme, il est donc possible d'afficher une propriété de votre modèle dans votre template en la nommant de la même façon. Comme nous avons le plaisir d'utiliser TypeScript, j'en ai profité pour typer les propriétés du component. Ainsi, person est une string ; age est un number ; et address est un any. J'ai fait de l'interpolation !

Mais l'interpolation, c'est quoi exactement ?

C'est un mécanisme qui va, dans un premier temps, analyser vos templates pour retrouver vos {{interpolations}}, afin de placer, dans un second temps, des écouteurs de changement sur les propriétés de votre component qui sont liées par leurs noms. Une fois cette résolution de liaisons faite, Angular 2 va valoriser vos interpolations avec ces propriétés lors d'un changement de ces dernières.

Il est maintenant temps de voir le résultat en lançant le serveur web (à l'aide de ng serve ) si cela n'est pas encore fait, pour constater le résultat suivant :

Personne : John Doe | Age : 30 | Adresse : [object Object]

Les interpolations {{person}} et {{age}} ont bien fonctionné. person étant une string, aucune transformation n'est à opérer pour remplacer {{person}} par "John Doe". age était un number et a été transformé en string à l'aide du mécanisme de cast, et il s'affiche donc plutôt bien. Par contre, notre adresse qui est un objet, elle a été castée en [object Object]. Pour afficher un object, notre template doit explicitement inscrire {{address.street}}, par exemple, pour afficher la rue.

L'interpolation est donc une fonctionnalité bien utile pour afficher une donnée et la synchroniser en permanence avec le modèle.

II - [Le Property Binding]

Le property Binding est également un mécanisme de Data Binding "one way". Tout comme l'interpolation, il permet de répercuter dans le DOM les valeurs des propriétés du component. L'annotation associée est […] mais pour les réfractaires, il est possible de préfixer par bind- les propriétés. Le Property Binding est utilisable :

- Sur un élément du DOM. Par exemple <img [src] ="…" /> ou <img bind-src ="…" /> .

- Sur un attribute directive. Par exemple <div [ngClass] ="…"> ou <div bind-ngClass ="…">.

- Sur la propriété d'un component. Par exemple <page [color] ="…"> ou <page bind-color ="…"> .

II-A - Le Property Binding sur un élément du DOM

Si vous désirez valoriser de manière dynamique une propriété d'un élément HTML du DOM, le Property Binding est l'homme de la situation. Imaginons que nous voulions aligner notre fiche identité à droite de l'écran : il suffit, dans notre cas, d'ajouter l'attribut align à l'élément de notre template <div> et le valoriser à right. Maintenant, pour une raison X, nous préférons variabiliser l'alignement avec une propriété alignement de notre component. Pour réaliser cela, nous avons deux possibilités :

- Utiliser l'interpolation, à savoir modifier notre div comme ceci :

app.component.ts

1. import { Component } from '@angular/core';

3. @Component({

4. selector: 'app-root',

5. template: ` <div align="{{alignement}}" >Personne : {{person}} |

6. Age : {{age}} | Adresse : {{address.street}}</div> `

7. })

8. export class AppComponent {

9. person:string= 'John Doe';

10. age:number= 30;

11. address:any= {street:'rue du Paradis', city:'75010 Paris'};

12. alignement:string = 'right';

13. }

- Utiliser le Property Binding, à savoir modifier notre div comme cela :

app.component.ts

1. import { Component } from '@angular/core';

3. @Component({

4. selector: 'app-root',

5. template: ` <div [align]="alignement" >Personne : {{person}} | Age :

6. {{age}} | Adresse : {{address.street}}</div> `

7. })

8. export class AppComponent {

9. person:string= 'John Doe';

10. age:number= 30;

11. address:any= {street:'rue du Paradis', city:'75010 Paris'};

12. alignement:string = 'right';

13. }

Alors pourquoi préférer le Property Binding à l'interpolation ? Il n'y a pas, techniquement, de raison de choisir une manière plutôt qu'une autre. Dans le cas du Property Binding sur un élément du DOM, la propriété alignement sera toujours une string, il ne peut pas y avoir de problème de cast dû à l'interpolation. C'est donc plus une histoire de visibilité du code. Je vous conseille toutefois d'établir des règles de codage afin d'harmoniser vos templates.

II-B - Le Property Binding sur un attribute directive

Le Property Binding sur un attribute directive fonctionne de la même manière. Pour rappel, un attribute directive est une directive modifiant le comportement ou l'apparence d'un élément. Angular 2 propose déjà une série d'attribute directives tels que ngClass ou ngStyle. Imaginons que l'on veuille changer la couleur du texte de notre div à l'aide des styles CSS. Angular 2 propose d'utiliser la directive ngStyle afin d'appliquer dynamiquement un attribut de style CSS sur l'élément courant. Pour réaliser cette prouesse, la directive ngStyle utilise le Property Binding. Nous pouvons donc nous créer une propriété couleur dans notre component et appliquer cette couleur en tant que style CSS :

app.component.ts

1. import { Component } from '@angular/core';

3. @Component({

4. selector: 'app-root',

5. template: ` <div [align]="alignement"

6. [ngStyle]="{color:couleur}">Personne : {{person}} | Age : {{age}} |

7. Adresse : {{address.street}}</div> `

8. })

9. export class AppComponent {

11. person:string= 'John Doe';

12. age:number= 30;

13. address:any= {street:'rue du Paradis', city:'75010 Paris'};

14. alignement:string = 'right'; couleur:string = 'red';

15. }

Le Data Binding va remplacer la variable couleur dans le template par sa valeur 'red' définie dans le component. Si vous désirez d'autres exemples de Property Binding sur un attribut directive, je vous conseille ce tutoriel sur les directives. Vous y apprendrez comment les propriétés sont passées aux directives grâce au @Input.

II-C - Le Property Binding sur une propriété d'un component

Comme je n'ai pas de component tout fait pour illustrer ce cas, nous allons nous faire plaisir et créer quelques components pour comprendre le Data Binding sur une propriété d'un component :

Vous allez maintenant comprendre comment fonctionne le property binding Angular 2 grâce au décorateur @Input. Commençons par modifier le template de notre component AppComponent pour y placer notre component Comp1Component. Pour illustrer notre property binding, je vous propose de passer la propriété address de notre AppComponent à notre nouveau component Comp1Component. Il s'agit d'un property binding classique.

app.component.ts

1. import { Component } from '@angular/core';

3. @Component({

4. selector: 'app-root',

5. template: ` <div [align]="alignement"

6. [ngStyle]="{color:couleur}">Personne : {{person}} | Age : {{age}} |

7. Adresse : {{address.street}}</div>

8. <app-comp1 [monAdresse]="address"></app-comp1> `

9. })

10. export class AppComponent {

12. person: string= 'John Doe';

13. age: number= 30;

14. address: any= {street: 'rue du Paradis', city: '75010 Paris'};

15. alignement: string = 'right';

16. couleur: string = 'red';

17. }

Dans ce Property Binding, nous retrouvons bien la propriété address à droite de l'égalité et entre [ ] (à gauche donc) un nom de propriété (comme finalement ngStyle, ngClass, …) qui sera valorisé par le mécanisme de Property Binding. Notre component Comp1Component récupérera donc address au niveau de sa portée. C'est ici que le decorator @Input entre en jeu. En le positionnant devant une variable, le mécanisme de data binding dans le sens "extérieur vers intérieur" se met en place.

Comp1.component.ts

1. import {Component, Input} from '@angular/core';

3. @Component({

4. selector: 'app-comp1',

5. template: ` {{monAdresse.street}} `

6. })

7. export class Comp1Component {

9. @Input() monAdresse: any;

11. }

Grâce au décorateur @Input, le template de Comp1Component affiche la propriété monAdresse.street sans problème.

II-D - (L'Event Binding)

Avec le Property Binding, vous avez pu, à partir d'un component, binder une propriété pour interagir avec un élément du DOM. Maintenant, vous allez voir comment, à partir d'un évènement du DOM, interagir avec un component. Pour réaliser ce Data Binding, Angular 2 utilise les évènements ; d'où le nom de Event Binding. Avec ce mécanisme, vous pouvez être averti des évènements utilisateurs tels que le clic, la frappe sur le clavier, le touch, … La syntaxe est proche du Property Binding : à gauche de l'égalité, on retrouve l'évènement que l'on veut émettre entre parenthèses ou précédé de on-, et à droite, on trouve la fonction que l'on appellera lorsque le component interceptera l'évènement. Par exemple, si vous désirez réagir à un clic sur un bouton de notre template AppComponent pour modifier le nom de la personne, il suffit de rajouter une méthode modifierPersonne() qui modifiera la propriété person lorsque l'on clique sur le bouton (on aura donc une interaction dans le DOM qui modifiera le modèle) :

app.component.ts

1. import {Component} from '@angular/core';

3. @Component({

5. selector: 'app-root',

6. template: ` <div [align]="alignement"

7. [ngStyle]="{color:couleur}">Personne : {{person}} | Age : {{age}} |

8. Adresse : {{address.street}}</div>

9. <app-comp1 [monAdresse]="address"></app-comp1>

10. <button (click)="modifierPersonne()">Modifier adresse</button> `

11. })

12. export class AppComponent {

14. person: string= 'John Doe';

15. age: number= 30;

16. address: any= {street: 'rue du Paradis', city: '75010 Paris'};

17. alignement: string = 'right';

18. couleur: string = 'red';

19. modifierPersonne() {

20. this.person = 'Another man';

21. }

22. }

Si vous cliquez sur le bouton, l'adresse sera automatiquement modifiée dans notre template en haut à droite de l'écran.

Vous pouvez aussi créer vos propres Event Binding très simplement afin d'envoyer un évènement d'un component vers un component parent. Pour illustrer cela, nous allons utiliser notre component Comp2Component créé précédemment. Ce component sera chargé d'incrémenter ou de décrémenter une variable à l'aide de deux boutons et envoyer le résultat via un évènement. Le component parent AppComponent s'occupera, lui, de récupérer cet évènement afin d'afficher la valeur. Voici pour commencer le source de Comp2Component :

comp2.component.ts

1. import {Component, Output, EventEmitter} from '@angular/core';  @Component({

2. selector: 'app-comp2',

3. template: ` <button (click)="decrement()" > - </button>

4. <button (click)="increment()"> + </button> `

5. })

6. export class Comp2Component {

8. counterValue: number = 0;

9. @Output() counterChange = new EventEmitter();

11. increment() {

12. this.counterValue++;

13. this.counterChange.emit({ value: this.counterValue });

14. }

16. decrement() {

17. this.counterValue--;

18. this.counterChange.emit({ value: this.counterValue });

19. }

21. }

Si vous avez l'œil, vous avez sans doute observé un nouveau decorator : @Output. Ce decorator se veut être le pendant du decorator @Input. Quand @Input se charge de recevoir une valeur, @Output se charge, lui, d'envoyer une donnée. Ainsi, counterChange est une instance de classe EventEmitter en charge d'envoyer un évènement lors une pression sur un des boutons.

EventEmitter est une classe utilisée dans une directive ou un component pour émettre un évènement. Sa méthode principale est emit(value? : T) qui permet d'envoyer un évènement contenant potentiellement un objet ou une valeur.

Il ne reste plus qu'à utiliser Comp2Component dans notre component principal AppComponent en déclarant notre Event Binding afin de récupérer la valeur émise par le component :

app.component.ts

1. import {Component} from '@angular/core';

3. @Component({

4. selector: 'app-root',

5. template: ` <div [align]="alignement" [ngStyle]="{color:couleur}">Personne : {{person}} | Age : {{age}} | Adresse : {{address.street}}</div>

6. <app-comp1 [monAdresse]="address"></app-comp1>

7. <button (click)="modifierPersonne()">Modifier adresse</button>

8. <h1>Event binding - Compteur</h1>

9. <div>

10. <app-comp2 (counterChange)="myValueChange($event);">

11. </app-comp2>

12. </div>

13. <br/>

14. <div>Valeur récupérée : {{compteur}}</div> `

15. })

16. export class AppComponent {

17. person: string= 'John Doe';

18. age: number= 30;

19. address: any= {street: 'rue du Paradis', city: '75010 Paris'};

20. alignement: string = 'right';

21. couleur: string = 'red';

22. compteur: any = 'N/A';

23. myValueChange (event) {

24. this.compteur = event.value;

25. }

26. modifierPersonne () {

27. this.person = 'Another man';

28. }

29. }

Comme par magie, la méthode myValueChange est appelée à chaque fois qu'un évènement counterChange est reçu. À noter la possibilité de passer $event en paramètre pour récupérer le contenu de l'évènement.

II-E - [(Le two-way Data Binding)]

Pour terminer ce tutoriel , je vais vous parler du two-way Data Binding. Ce mécanisme permet, à partir d'une même notation, de modifier le modèle à partir du DOM et de modifier le DOM à partir du modèle. Voici la syntaxe utilisée dans Angular pour déclarer un tel Data Binding :

<ma-taille [(taille)] ></ma-taille>

Il faut se souvenir que les parenthèses sont entre les crochets. Pour se souvenir de cela, cette notation se nomme "banana in a box"… Cette banane dans une boite rappelle ce que nous avons pu voir avec le Property Binding et l'Event Binding. Effectivement, ce que nous venons de voir aurait pu également s'écrire :

<input [taille]="maTaille" (tailleChange)="maTaille=$event">

Nous pouvons dès lors en déduire que le code que notre component aura un @Input nommé taille et un @output nommé tailleChange. Cela constitue donc la règle du Data Binding 2-way : si l'on considère la notation [(x)], x est la propriété à setter et xChange est l'évènement lancé lors d'une modification de sa valeur. Cet évènement sera donc intercepté par son component parent afin de récupérer cette valeur.

Voici le code du component ma-taille :

mataille.component.ts

1. import {Component, Input, Output, EventEmitter} from '@angular/core';

4. @Component({

5. selector: 'ma-taille',

6. template: ` <div>

7. <button (click)="dec()" title="plus petit">-</button> <button (click)="inc()" title="plus grand">+</button> <label [style.font-size.px]="taille">FontSize: {{taille}}px</label>

8. </div> `

9. })

10. export class maTailleComponent {

12. @Input() taille: number;

13. @Output() tailleChange = new EventEmitter<number>();

15. dec() {

17. this.resize(-1);

18. }

20. inc() {

21. this.resize(+1);

22. }

24. resize(delta: number) {

25. this.taille = +this.taille + delta; this.tailleChange.emit(this.taille);

26. }

28. }

Pour l'utiliser, il est nécessaire de déclarer, dans le component parent, une propriété maTaille qui sera bindée, puis de l'utiliser comme ceci :

1. <ma-taille [(taille)]="maTaille"></ma-taille>

2. <div [style.font-size.px]="maTaille">texte resizer</div>

II-E-1 - [(Le two-way Data Binding avec NgModel)]

NgModel est une directive qui permet de réaliser du Data Binding sur les éléments de formulaire de type Input et textarea. Cette directive fonctionne, dans la pratique, de la même manière que la directive ngModel d'AngularJS. Voici la syntaxe de cette directive :

<input [(ngModel)]="prenom">

Important : pour utiliser cette directive, il est nécessaire de rajouter le module FormModule à votre projet, mais rassurez-vous, Angular-Cli l'a déjà fait pour vous.

Comme vu précédemment, cela aurait pu également s'écrire :

1. <input [ngModel]="currentHero.firstName" (ngModelChange)="currentHero.firstName=$event">

Nous pouvons l'utiliser dans le template de AppComponent. Il va permettre de modifier la propriété person et vous pourrez ainsi constater que l'affichage de la personne (en haut à droite) sera également modifié.

app.component.ts

1. import {Component} from '@angular/core';

3. @Component({

5. selector: 'app-root', template: ` <div [align]="alignement" [ngStyle]="{color:couleur}">Personne : {{person}} | Age : {{age}} | Adresse : {{address.street}}</div>

6. <app-comp1 [monAdresse]="address"></app-comp1>

7. <button (click)="modifierPersonne()">Modifier adresse</button>

8. <h1>Event binding - Compteur</h1>

9. <div>

10. <app-comp2 (counterChange)="myValueChange($event);"> </app-comp2>

11. </div>

12. <br/>

13. <div>Valeur récupérée : {{compteur}}</div>

14. <h1>Data Binding 2-way</h1>

15. <div>

16. <input [(ngModel)]="person"/>

17. </div> `

18. })

19. export class AppComponent {

21. person: string= 'John Doe';

22. age: number= 30;

23. address: any= {street: 'rue du Paradis', city: '75010 Paris'};

24. alignement: string = 'right';

25. couleur: string = 'red';

26. compteur: any = 'N/A';

27. myValueChange (event) {

28. this.compteur = event.value;

29. }

31. modifierPersonne () {

32. this.person = 'Another man';

33. }

35. }

Simple et efficace !

III - Conclusion

Nous avons appris beaucoup de choses dans ce tutoriel à propos du Data Binding. Nous savons maintenant reconnaître du premier pour d'œil de type de Data Binding utilisé en lisant uniquement le code HTML. Nous avons également vu qu'il existe encore une forme de Data Binding 2-way dans le framework Angular même si les algorithmes utilisés n'ont rien à voir avec ceux de son prédécesseur AngularJS.

IV - Remerciements

Tous mes remerciements à toute l'équipe de developpez.com, et plus particulièrement Laethy pour la relecture technique et ced pour la relecture orthographique.