Fondamentaux d'Angular
Introduction
Dans ce chapitre seront vus les éléments fondamentaux d’Angular, qui permettent de comprendre et de créer une application Angular simple.
Les composants
Un composant correspond à une classe exposant une vue et définissant la manière dont l’utilisateur interagit avec cette vue. Il s’agit de l’élément de base d’une application Angular, une application étant elle-même un composant.
Un composant peut être lui-même composé d’un ou de plusieurs composants, une application correspondant donc à un assemblage de ces éléments. L’idée est qu’un composant doit être indépendant pour être réutilisable, peu importe les composants le référençant.
Pour les développeurs AngularJS, un composant Angular remplace ce qu’on connaissait sous le nom de contrôleur (controller). Par contre, la notion de scope n’existe plus.
Le schéma précédent illustre le principe de composabilité apporté par les composants d’Angular. Un composant racine, appelé ComposantRacine, définit la base de l’application. Ce composant s’appuie sur les composants nommés ComposantEnfant1 et ComposantEnfant2. Ce dernier composant est lui-même composé de plusieurs autres composants.
Comme dit précédemment, un composant Angular correspond à une classe TypeScript. Cette classe peut déclarer des propriétés et des méthodes qui seront exposées en tant que modèle du composant et donc accessibles depuis la vue.
export class TodolistComponent {
}
La classe TypeScript précédente représente un composant.
Pour indiquer à Angular que cette classe doit être considérée comme un composant, il faut lui ajouter un décorateur.
La notion de décorateur sera vue en détail plus loin dans ce chapitre.
Le décorateur @Component permet...
Les templates
Un template est du HTML représentant une vue. Cette vue décrit à Angular comment doit être rendu le composant auquel il est associé.
L’association entre un composant et un template se fait via les propriétés template ou templateUrl du décorateur @Component. Ils permettent respectivement de déclarer un template directement dans l’attribut ou de définir une URL vers un fichier correspondant au template.
import { Component } from '@angular/core';
@Component({
selector: ‘app-todolist',
template: ‘<h1>Ma Todo List</h1>'
});
export class TodolistComponent {
[...]
}
Le template est ici défini directement dans le décorateur @Component.
Si le template est trop grand, il est préférable de l’externaliser dans un fichier séparé et de le référencer via la propriété templateUrl.
import { Component } from '@angular/core';
@Component({
selector: ‘app-todolist',
templateUrl: './todo-list.component.html',
});
export class TodoListComponent {
[...]
}
Le template est ici défini via la propriété templateUrl.
<h1>Ma Todo List</h1>
Le code précédent correspond au fichier todo-list.component.html.
1. Binding
La notion de binding permet de créer une relation entre les données d’un composant et les valeurs affichées dans les vues. Grâce à cela, nous n’avons pas à nous soucier des problématiques techniques liées à la synchronisation des données entre la vue et le contrôleur.
La déclaration d’un...
Les décorateurs
Les décorateurs Angular permettent de décrire au framework comment interpréter un élément.
Le décorateur @Component a par exemple été utilisé tout au long de ce chapitre pour indiquer que la classe associée doit être interprétée comme un composant.
import { Component } from '@angular/core';
@Component({
selector: ‘app-todolist',
template: ‘<h1>Ma Todo List</h1>'
});
export class TodolistComponent {
[...]
}
Sans le décorateur @Component, la classe aurait été considérée comme une classe TypeScript simple, sans lien avec un composant.
Le gros avantage des décorateurs est de bien séparer la partie métier des informations techniques.
Il existe de nombreux autres décorateurs qui seront découverts tout au long du livre, comme par exemple @Input, @Inject, @Injectable, @ViewChild.
Les pipes
Dans une application Angular, il n’est pas rare d’effectuer une transformation sur un élément avant de l’afficher. L’exemple le plus simple est celui d’une date. Lorsqu’une date est affichée, c’est rarement sous le format traditionnel JavaScript. En effet, une date JavaScript s’affiche au format suivant : Sun Jul 10 2016 17:03:07 GMT+0200 (Paris, Madrid (heure d’été)) alors qu’un utilisateur a plutôt l’habitude de la lire sous un format plus accessible, comme 10/06/2017. Le pipe est l’élément qui va permettre de gérer ces transformations.
Pour les développeurs AngularJS, les pipes Angular correspondent aux filtres AngularJS.
1. Utiliser un pipe
Un pipe s’utilise directement dans le template d’un composant. Il suffit d’appliquer l’opérateur pipe (|) entre la valeur à transformer et le pipe que l’on souhaite utiliser.
Dans l’exemple ci-dessous, c’est le pipe date (un pipe fourni par Angular) qui est utilisé afin de rendre la date du jour plus lisible.
import { Component } from '@angular/core';
@Component({
selector: 'today',
template: `<p>Nous sommes le {{today | date}}</p>`
})
export class TodayComponent {
today = new Date();
}
Cependant, dans cet exemple, le template généré ressemblera à cela : Nous sommes le Jul 10, 2016. En effet, Angular va utiliser par défaut le format de date américain. Pour spécifier le format à appliquer à la date, il faut passer des paramètres au pipe.
Les paramètres se placent directement derrière le nom du pipe séparés par un double-point (:). Pour modifier le format...
Les modules
Un module permet d’organiser une application en blocs fonctionnels ou techniques. Il est également le point d’entrée d’une application Angular.
De la même manière qu’un composant, un module se déclare via une classe TypeScript et doit être décoré par l’attribut @NgModule :
@NgModule({
imports: [BrowserModule],
declarations: [AppComponent],
bootstrap: [AppComponent],
})
export class AppModule { }
Cet attribut possède notamment les propriétés suivantes :
-
imports : permet d’importer des modules, afin de pouvoir utiliser tous les éléments de ces modules dans le module courant.
-
declarations : définit tous les éléments embarqués dans le module (composants, directives, pipes, etc.).
-
bootstrap : définit le composant racine de l’application.
-
providers : définit les éléments (services par exemple) qui seront injectables dans tous les composants du module.
Si vous utilisez la CLI d’Angular, le fichier du module sera automatiquement mis à jour lorsque vous créerez des composants, filtres, etc.
Un exemple de module que l’on rencontre couramment correspond au module de routing. Il permet d’isoler le module applicatif, contenant tout le fonctionnement métier, de la déclaration des routes qui risquerait de polluer le code.
const routes: Routes = [
{ path: '', redirectTo: '/list', pathMatch: 'full' },
{ path: 'list', component: ListComponent },
{ path: 'detail/:id', component: DetailComponent }
];
@NgModule({
imports: [ RouterModule.forRoot(routes) ],
exports: [ RouterModule ]
})
export class AppRoutingModule {}...
Observable et promise
JavaScript est un langage par nature fortement asynchrone. L’aspect asynchrone est géré historiquement via le mécanisme de callback.
myAsynchronousMethod(function(data) {
console.log(data)
});
La méthode précédente est asynchrone. Le callback passé en paramètre est exécuté lorsque l’appel asynchrone est terminé.
Les callbacks, bien que fonctionnels, présentent beaucoup de limitations : gestion des erreurs, chaînage d’appels asynchrones, etc.
Pour cela, ES6 (EcmaScript 6) propose une spécification : les Promises. Une promise est une "promesse" permettant d’exécuter du code seulement lorsque l’action asynchrone est terminée.
Les promises possèdent trois états : pending lorsque l’action asynchrone est en cours, fulfilled lorsque l’action a été réalisée avec succès et rejected lorsque l’action a généré une erreur.
myAsynchronousMethod.then(function(data) {
console.log(data);
}, function(error) {
console.error(error);
});
La méthode précédente est asynchrone et expose une promise. Les paramètres passés en argument correspondant au callback de succès et d’erreur.
Angular utilise également la notion d’observable, introduite par la librairie RxJS. À la différence d’une promise, un observable peut être résolu plusieurs fois afin de renvoyer plusieurs valeurs. Les observables possèdent également un ensemble de méthodes permettant d’y effectuer des opérations.
myAsynchronousMethod.subscribe(function(data) {
console.log(data);
}...
Préparer l’application pour la production
Par défaut, Angular s’exécute dans un mode de développement. Ce mode de développement est fait pour qu’Angular expose des informations sur l’état de l’application et effectue un nombre de vérifications supplémentaires afin d’aider l’équipe de développement.
L’une de ces vérifications est par exemple d’effectuer deux cycles d’exécutions au lieu d’un seul. Cela permet de s’assurer que les valeurs bindées n’ont pas changé entre ces deux cycles, dans le but de déceler des effets de bords pouvant engendrer des erreurs.
Le côté négatif du mode de développement est l’impact sur les performances. Ces vérifications supplémentaires prennent évidemment du temps et ne s’avèrent pas nécessaires lorsque l’application est déployée en production.
L’activation du mode production se fait en appelant la méthode enableProdMode.
Import { enableProdMode } from '@angular/core';
enableProdMode();
Il est nécessaire d’effectuer l’import de la méthode enableProdMode depuis @angular/core avant de pouvoir l’appeler.
Cette méthode est à appeler avant le bootstrap de l’application.
Import { enableProdMode } from '@angular/core';
enableProdMode();
bootstrap(...);