La mise en œuvre d’Angular
Introduction
Depuis maintenant plusieurs années, le monde du développement web a vu arriver un nombre incalculable de frameworks et autres librairies JavaScript afin de faciliter la vie des développeurs front-end. L’un d’entre eux, AngularJS, s’est imposé comme étant l’un des frameworks les plus populaires de sa génération, en proposant une méthodologie robuste afin de rendre le code client bien plus maintenable et performant. Concrètement, le but d’AngularJS est de proposer la méthodologie MVC côté client tout en apportant les bienfaits d’une Single Page Application avec la puissance du langage JavaScript.
Aujourd’hui, et annoncée depuis presque deux ans, la version 2 du célèbre framework est sur le point d’être finalisée, proposant déjà une base extrêmement solide pour construire des applications web modernes. En corrélation avec l’équipe de Microsoft en charge du langage TypeScript, l’équipe d’Angular a complètement réécrit et repensé le framework afin d’y apporter la performance qui lui manquait, mais aussi pour profiter de toute la puissance de la syntaxe de TypeScript afin de concevoir un code propre bien plus maintenable qu’il ne pouvait l’être auparavant.
Le présent chapitre va traiter...
Les nouvelles API HTML 5
Longtemps le Web a été conçu à partir de balises <div> et <span> n’apportant finalement que très peu de verbosité au code HTML que le développeur écrivait. Certes, il existe les classes CSS permettant de styliser facilement les balises, mais d’un point de vue HTML pur, le code n’est que très peu structuré.
Ne serait-il pas mieux de pouvoir créer et utiliser une balise <left-menu> pour un menu vertical, une balise <grid> pour concevoir une grille ou encore une balise <notification> afin de concevoir un centre de notifications pour l’application ? HTML 5 permet maintenant ce genre d’écriture en autorisant les custom elements.
Les custom elements permettent aux développeurs web de concevoir leurs propres types d’éléments HTML, posant ainsi la première brique du concept plus large de web components (expliqué dans la partie suivante). Ces nouveaux éléments permettent de :
-
définir de nouveaux éléments HTML/DOM ;
-
créer des éléments qui étendent d’autres éléments ;
-
concevoir des fonctionnalités personnalisées rassemblées dans un même package logique et identifiées via un seul tag ;
-
étendre les API d’autres éléments du DOM.
La base de toute création d’un nouvel élément HTML 5 provient de la méthode document.registerElement() en JavaScript. Le code ci-dessous montre comment créer un élément à partir de rien :
var TestAngular = document.createElement('test-angular);
document.body.appendChild(new TestAngular());
Ce qui résulte du code HTML suivant qui est introduit dans le DOM de la page :
<test-angular></test-angular>
Le nommage des éléments est très important. Tous les navigateurs supportent une liste finie d’éléments HTML qui sont connus. Les éléments non connus, tels que <ville> ou <client> ne vont pas forcément provoquer une erreur du parseur HTML et vont plutôt être définis comme étant des éléments inconnus via l’interface HTMLUnknownElement. La norme W3C préconise plutôt...
La notion de composant
Angular est résolument tourné vers les nouveaux standards du Web moderne en implémentant une bonne partie des nouvelles API et en proposant avec ceci toute une méthodologie de développement côté client à la fois riche et flexible. Le cœur d’Angular est maintenant tourné vers un seul et unique concept : les web components.
Pour faire simple, un web component est la fusion des grands principes qui ont été décrits dans la section précédente : les imports, les custom elements et le Shadow DOM. L’utilisation d’un web component présente plusieurs avantages :
-
L’utilisation du Shadow DOM permet de travailler uniquement sur une petite partie du DOM et non sur le DOM entier de la page. De ce fait, lors de la manipulation d’éléments HTML, le processus est plus performant car on ne parcourt qu’une partie du DOM.
-
L’encapsulation du style pour ainsi éviter les collisions entre les différents CSS.
-
La réutilisation des composants grâce à l’import.
-
La création automatique et propre des éléments personnalisés.
-
La séparation du DOM et du code source est beaucoup plus simple et claire, tout en conservant une maintenabilité efficace.
-
Les pages sont plus légères lors du chargement.
Cependant, les web components peuvent vite devenir désorganisés et complexes si le développeur construit des éléments de trop grande taille. C’est face à ce problème qu’Angular intervient afin de répondre de manière efficace et organisée à la complexité des web components.
D’un point de vue d’Angular le constat est très clair : tout est composant. Cela veut dire que chaque parcelle de l’application web doit être un web component afin d’améliorer les performances et le dynamisme du site. Même l’ensemble de l’application doit être un unique composant, qui va lui-même interagir avec d’autres composants pour la récupération de données ou encore la navigation. Au final, le développeur construit un arbre de composants qui peuvent communiquer entre eux.
Exemple d’arbre de dépendances des composants Angular....
Les différentes métadonnées
La section précédente a permis de mettre en lumière la notion de composant Angular et la philosophie qui est présente derrière tout ce framework. Les exemples ont montré qu’un composant n’est finalement qu’une simple classe TypeScript. Comment Angular peut-il alors interpréter cette classe comme étant un wzb component ? C’est grâce aux métadonnées.
Une métadonnée permet de donner des informations à Angular afin qu’il exécute un certain nombre d’opérations sur la classe en fonction du type de la métadonnée. Les métadonnées sont rajoutées via les décorateurs, c’est notamment le cas de @Component qui était présent dans les exemples précédents. Le code ci-dessous ajoute des métadonnées à une classe TypeScript afin de lui rajouter des fonctionnalités Angular :
@Component({
selector: 'product-list',
templateUrl: 'app/product-list.component.html',
directives: [ProductDetailComponent],
providers: [ProductService]
})
export class ProductsComponent { ... }
Ici le décorateur indique que la classe ProductsComponent est un composant Angular. Les décorateurs ne sont ni plus ni moins que des fonctions, prenant en entrée un objet de configuration qui sera traité ensuite par Angular afin d’exécuter les actions nécessaires (récupérer la vue, injecter des services…).
L’exemple ci-dessus regroupe les informations de configuration suivantes :
-
selector : sélecteur CSS permettant à Angular de créer le composant dès qu’il trouve le tag <product-list> dans le HTML parent. Par exemple, Angular sera capable de remplacer le code HTML suivant par le composant correspondant :
<product-list></ product -list>
-
templateUrl : le chemin vers la vue du composant.
-
directives : tableau de composants ou de directives dont le template a besoin. Concrètement, le composant ProductsComponent aura probablement besoin d’autres composants pour fonctionner (par exemple un composant pour afficher le détail...
Le lancement d’une application Angular
Les décorateurs sont un moyen très efficace pour structurer une application Angular et concevoir des composants HTML 5 de manière épurée. Le framework s’occupe tout seul d’instancier les composants, de récupérer les vues et de gérer les dépendances et le développeur doit juste s’occuper de coder les fonctionnalités dont il a besoin afin de répondre à son besoin.
Écrire les composants est une chose, mais il faut ensuite que ces composants s’instancient et vivent au travers de la page afin de répondre aux différentes interactions de l’utilisateur. Ce processus s’appelle le bootstrap (l’amorçage) et permet de lancer les composants Angular.
Afin de faire fonctionner son premier composant, il faut tout d’abord importer et intégrer tous les scripts dont Angular a besoin pour fonctionner. Les packages NPM sont les suivants :
-
Tous les modules Angular : @angular/common, @angular/core ou encore @angular/http pour le fonctionnement du framework.
-
SystemJS : un module-loader (chargement des librairies et des dépendances à la volée) universel et populaire en JavaScript.
-
Es6-shim : compatibilité ECMAScript 6 pour les moteurs JavaScript.
-
Reflect-metadata : fonctionnalité de réflexion pour les métadonnées écrites en ECMAScript 6.
-
RxJS : programmation asynchrone et basée sur événement.
-
Zone.js : contexte d’exécution persistant à travers les tâches asynchrones. Cette librairie est notamment utilisée pour la mise à jour automatique de la vue lorsqu’une propriété est affectée depuis une autre tâche.
Les dépendances à insérer dans le fichier package.json sont donc les suivantes :
"dependencies": {
"@angular/common": "2.0.0-rc.1",
"@angular/compiler": "2.0.0-rc.1",
"@angular/core": "2.0.0-rc.1",
"@angular/http": "2.0.0-rc.1", ...
La navigation
La navigation est une chose commune dans un site web : l’utilisateur explore l’application via des URL qui définissent la page sur laquelle il se trouve. Ce procédé lui permet également d’écrire directement l’URL qu’il souhaite, et le site web répondra avec la page correspondante. Il est donc important d’avoir un système consistant et fiable permettant de traduire chaque URL avec une page web fournie par le serveur : c’est ce qu’on appelle des routes.
Angular possède un composant dédié au routage dans l’application : le component router (ou le router). Ce dernier permet d’interpréter les URL du navigateur afin de naviguer vers une page générée par le client. En effet, il faut rappeler qu’Angular ne fonctionne que côté client, et que donc la navigation serveur ne ferait que court-circuiter le fonctionnement d’Angular, réinitialisant ainsi tous les composants à chaque rafraîchissement de la page. Le framework intègre un bon nombre de concepts qui seront étudiés dans cette partie.
Le concept de navigation avec Angular repose entièrement sur les composants. Lorsqu’une application Angular va naviguer d’URL en URL, elle va en fait naviguer de composant en composant, et les composants eux-mêmes vont pouvoir définir des routes vers de nouveaux composants, et ainsi de suite. Toutes les routes n’ont pas besoin d’être définies au niveau du composant racine, et il n’est pas nécessaire de définir des templates de routes non plus.
Tout d’abord, et afin qu’Angular puisse fonctionner correctement, le développeur doit intégrer une balise un peu spéciale qui est <base href="/"> dans l’index.html, après la balise <head>. Cette balise va permettre de définir des routes relatives, composant par composant.
Pour comprendre ce fonctionnement, imaginons une application avec quatre pages : une liste de produits, un détail de produit, une liste des ventes de ce produit, et enfin une page avec le détail de la vente. La liste des ventes serait accessible via le détail du produit. On aurait alors les routes suivantes :
-
/products : liste de tous les produits.
-
/products/25 : détail...