Appuyez sur Entrée pour voir vos résultats ou Echap pour annuler.

Créer un module Angular

Avant de commencer avec la création d’un module Angular, j’aimerais prendre 2 minutes pour vous souhaiter une bonne année 2018! Uncle Bob (Clean Coder) le dit, il faut apprendre un langage par an. Pour ma part, j’ai appris les rudiments de Ruby et de Ruby on Rails en 2017 et, cette année, je compte me lancer dans le Python. Et vous? Commentez pour me dire votre objectif!

Aujourd’hui, si vous n’avez pas encore entendu parler d’Angular, vous vivez sous une roche. L’objectif de cet article est de créer un module Angular qu’on pourra publier sur NPM, soit un privé, soit le registre public. Si vous n’avez aucune base Angular, je vous invite à lire cet article qui devrait vous mettre sur pieds en 15 minutes.

Commençons.

Qu’est-ce qu’un module

Un module Angular est simplement une encapsulation de composants, directives, pipes, etc. qui partagent des caractéristiques communes. Par exemple, le FormsModule contient plusieurs outils nous permettant d’utiliser les formulaires dans une application Angular. Dans cet article, je vais créer un module ezo-inplace-input qui contiendra un composant qui sera responsable d’afficher un libellé éditable se transformant en boîte de saisie lorsqu’on clique dessus.

NPM public vs privé

Une entreprise qui voudrait intégrer Angular dans son développement devrait en premier lieu avoir un registre NPM privé. Il existe plusieurs moutures, mais Sinopia est simple et rapide. Ceci permet aux développeurs de publier leurs modules sur un serveur NPM appartenant à l’entreprise, facilitant ainsi la réutilisation de ces modules au sein de l’organisation.

Comme alternative, vous pourriez utiliser le registre public d’NPM et configurer vos modules pour qu’ils soient privés. Si vous vous sentez généreux, vous pouvez aussi publier directement sur NPM en mode public.

Structure de base

Commençons avec un répertoire vide. Ouvrez une invite de commande et exécutez :

Nous allons utiliser angular-cli  pour nous configurer une application Angular standard. Ceci nous permettra entre autre de générer un module qui sera facilement testable avec l’intégration de WebPack, Angular, TypeScript, Jasmine, Protractor, et j’en passe.

Le module et le composant

Gardez en tête qu’un module doit se conformer aux principes SOLID, comme tout composant dans un logiciel. Lorsqu’on parle de « partager une caractéristique commune », il faut rester très précis. Par exemple, ne créez pas un module UserModule qui comprendrait des composants de formulaires liés aux utilisateurs, des services ou directives, etc.. Si vous devez utiliser un UserService dans plus d’un module, il doit en être un lui-même.

À l’intérieur de ce module, nous allons créer un composant. Ce dernier affichera un libellé avec une certaine valeur qui sera éditable lorsque l’utilisateur cliquera sur ce libellé.

Si vous n’avez pas trop l’habitude de créer des modules, sachez qu’en créant un composant dans le répertoire d’un module avec  angular-cli, le composant sera déclaré à l’intérieur de ce module uniquement. Ceci nous permet d’isoler le code lié à ce composant pour faciliter sa réutilisation.

Par contre, en ayant généré notre module, comme un service, il ne sera pas importé dans le module principal d’application. Pour des fins de preuve de concept, importons InplaceInputModule dans AppModule :

app.module.ts

Puis, pour utiliser le composant, il faut d’abord l’exporter du module InplaceInputModule pour le rendre public:

inplace-input.module.ts

Et… ftw 😉 je change le préfix app du composant  inplace-input pour ezo dans sa propriété selector :

Si votre EDI supporte tslint, vous pouvez ajouter n’importe quel préfix dans le tslint.json , sous la propriété  component-selector  :

Le composant

KISS (Keep It Simple and Stupid)

Nous allons créer un composant auquel nous allons pouvoir accrocher la propritété ngModel pour lui définir une valeur qui sera synchronisée entre le composant et la vue. Par défaut, le composant affiche un libellé, ici un p . Lorsque l’utilisateur clique sur ce libellé, ce dernier se cache pour laisser place à un champ texte permettant d’éditer la valeur. Si on appuie sur enter, la valeur est propagée.

Importons FormsModule .

inplace-input.module.ts

Puis, la vue :

inplace-input.component.html

Le code du composant :

Rien de bien sorcier, mais ça fonctionne comme on l’espérait. Par contre, vous l’utiliseriez comme ceci :

app.component.html

Pour le rendre facilement réutilisable, il nous faut une façon de pouvoir lui injecter une valeur. Pour ce faire, notre composant doit implémenter ControlValueAccessor de FormsModule :

Ceci nous oblige donc à implémenter les methodes suivantes :

Voici ce quelles font en détails et quand elles sont appelées :

  • writeValue(obj: any): void
    • Dans le cadre d’un two-way binding entre la vue et la classe, cette méthode est appelée lorsque la valeur externe change. Il nous faut donc mettre à jour notre valeur interne.
  • registerOnChange(fn: any): void
    • Nous devons offrir un événement sur lequel les autres composants viendront souscrire. Cet événement sera déclancher lorsque notre valeur interne change.
  • registerOnTouched(fn: any): void
    • touched est un état de composant qui signifie que l’utilisateur l’a visité ou touché. On doit permettre à d’autre composant d’écouter cet événement en offrant un deuxième émetteur. Nous ne l’utiliserons pas vraiment dans cet exemple.
  • setDisabledState?(isDisabled: boolean): void
    • Doit permettre de définir l’état du composant comme désactivé, comme pour un champ texte par exemple. Nous ne l’utiliserons pas, à des fins de simplicité.
Les émetteurs

Tel que mentionné ci-dessus, nous devons gérer deux événements : Touched et Changed. Ajoutons deux membres à notre composant :

Elles ne font rien. On les appelle aussi noop pour NO OPeration. Par contre, ces méthodes bidons seront écrasées par les méthodes paramètres des méthode registerOnChange et registerOnTouched . Ça semble compliqué? L’interface ControlValueAccessor simplifie grandement la chose. Par ces deux méthodes, le framework injectera des fonctions qui permettrons de lier un composant parent à un composant enfant de façon bi-directionnelle avec ngModel , comme un champ normal!

Reste qu’à les appeler! On modifie simplement le mutateur de notre valeur interne :

Dernier point, la méthode writeValue est appelée lorsque la valeur externe change et qu’il faut mettre à jour notre copie interne. On procédera de cette façon :

En utilisant le mutateur au lieu de changer directement la valeur de la propriété privée, on s’assure que l’appel à onChange()  est fait.

Dernier truc wierd

Le composant doit définir un provider comme ceci :

C’est nécessaire parce qu’on utilise cette classe avant qu’elle soit définie, somehow… 😉

Si on modifie alors app.component.html  pour injecter une valeur via ngModel :

Vous avez un prototype qui fonctionne! Testez-le en clonant ce repo GitHub

Publier?

Aujourd’hui, vous avez appris à générer un module avec un composant qui est réutilisable facilement en l’important dans un autre module. Il ne reste qu’à standardiser le tout avant de le publier sur un NPM privé ou public, mais c’est le sujet du prochain article. Je vais vous montrer comment extraire ce module générer avec angular-cli  et le publier sur NPM en tant que module indépendant et réutilisable via les dépendences du packages.json .

Si vous avez aimé, partagez et commentez! Merci de nous lire chaque semaine 🙂

Suivez-nous par courriel!

Saisissez votre adresse courriel pour vous abonner au blog d'Ezo et recevoir une notification de chaque nouvel article par email.