Le pattern Decorator de TypeScript pour Angular

Pour le support d’Angular, TypeScript 1.5 s’est vu doté d’une nouvelle fonctionnalité : Les décorateurs.
Si vous n’avez pas de base de connaissance en TypeScript, je vous conseille de lire rapidement l’article « Découverte de TypeScript ».

Qu’est-ce qu’un décorateur ?

C’est une façon de faire de la méta-programmation. Une sorte d’annotation, ne servant pas réellement au langage lui-même mais plutôt aux frameworks et au librairies. Le pattern Décorateur (Decorator) attache dynamiquement des responsabilités supplémentaires à un objet. Il fournit une alternative souple à l’héritage, pour étendre des fonctionnalités.

Avec Angular, cela nous permettra ici d’ajouter des métadonnées à nos classes, des propriétés ou encore des paramètres. On peut procéder en ajoutant ces paramètres manuellement au besoin (si on veut uniquement du ES5) mais ça s’avère facilité et plus lisible en utilisant TypeScript et les décorateurs.

En résumé, les Décorateurs sont pratiques pour encapsuler un comportement générique à ajouter sur une classe, une propriété ou une méthode et leur syntaxe est simple, du premier coup d’œil (si on a nommé proprement notre décorateur) on comprend ce qu’ils vont faire.

Comment reconnaitre un décorateur ?

Comme je l’écrivais précédemment, on trouve des décorateurs sur différents objets comme les classes, propriétés ou méthodes. Je vous donne un exemple de ce que l’on va croiser avec Angular dans la suite des tutoriels :

Par exemple, sur une classe :

@Component({
   selector: 'unlocked-content',
   templateUrl: './unlocked-content.component.html',
   styleUrls: ['./unlocked-content.component.css']
})
export class UnlockedContentComponent extends Content {
   // Inside my class,
   // I do what I want 
}

Ici, on comprend facilement que notre classe UnlockedContentComponent est un composant Angular ayant pour sélecteur HTML 'unlocked-content', construit à partir du template unlocked-content.component.html se trouvant dans le même dossier et stylé via le css unlocked-content.component.css

Sur une propriété :

@Input title: string; 

On indique via le décorateur que 'title' sera un champs input dans notre composant.

Ou encore, sur une méthode :

@LogInConsole()
getUnlockedContent(): any {
   // I do what I want
   return myObject;
}

Ce décorateur inscrira dans la console du navigateur toutes les méthodes qu’il décore !

Comment créer un décorateur ?

On va créer une méthode de log qui va inscrire dans la console du navigateur les nom des méthodes appelées.

const LogInConsole = function(){
   return (target: any, methodName: string, descriptor: any) => {
      console.log('function ' + methodName + ' called at ' + Date.now());
      return descriptor;
   }
}

Pour les décorateurs de méthode, la fonction prend 3 arguments :

  • target : le contexte de la méthode ciblée par le décorateur
  • methodName : le nom de la méthode ciblée
  • descriptor : la description de la méthode (ses propriétés en fait, tel qu’il serait retourné par getOwnPropertyDescriptor()). À noter que le décorateur doit être retourné par la fonction pour ne pas interrompre l’exécution de la méthode supérieure.
@LogInConsole()
getUnlockedContent(): any {
   // I do what I want
   return myObject;
}

Cette fonction, LogInConsole(), pourrait être appelée en début de chaque méthode mais il faudrait lui passer manuellement le methodName etc… d’où l’intérêt du Decorator !