• 12 heures
  • Difficile

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 20/06/2022

Créez votre propre Pipe

Pour afficher une donnée sous un format spécifique, Angular vous propose les Pipes. Le framework en fournit plusieurs, comme :

  • DatePipe, pour les dates ;

  • UpperCasePipe, LowerCasePipe et TitleCasePipe pour les chaînes de caractères ;

  • DecimalPipe, PercentPipe et CurrencyPipe pour les nombres.

Mais saviez-vous qu'Angular vous permet d'aller plus loin, en créant vos propres Pipes ? Imaginons quelques cas de figure :

  • vous voulez afficher un aperçu d'un texte, avec au plus 50 caractères, suivis de     pour montrer qu'il y a une suite ;

  • vous recevez des objets User de votre backend qui contiennent séparément les noms et prénoms des utilisateurs, et vous avez souvent besoin d'afficher le nom complet sous la forme "NOM Prénom" ;

  • au lieu d'afficher une date exacte, vous voulez afficher un texte descriptif de type "il y a quelques minutes".

Dans ces trois situations, la solution du Pipe est pertinente : vous voulez afficher une donnée sous un format spécifique sans modifier la donnée sous-jacente.

Formatez vos données

Puisque les Pipes que vous créez ici pourront être utiles dans toute l'application, vous allez les déclarer dans SharedModule. Vous allez commencer par le Pipe qui affichera un aperçu d'un texte, suivi des    .

Dans le dossier  shared  , créez un dossier  pipes  , et créez-y un fichier  shorten.pipe.ts  :

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'shorten'
})
export class ShortenPipe implements PipeTransform {
  transform(value: string): string {
    if (value.length <= 50) {
      return value;
    }
    return value.substring(0, 50) + '…';
  }
}

Pour créer un Pipe personnalisé :

  • vous utilisez le décorateur  @Pipe  , auquel vous passez un  name  qui sera le nom utilisé dans les templates pour appliquer ce Pipe ;

  • vous créez une classe qui implémente l'interface  PipeTransform  ;

  • pour implémenter  PipeTransform  , votre classe doit contenir une méthode  transform()  – c'est cette méthode qui prendra la donnée comme argument et la retournera transformée.

Ici, si le texte reçu fait moins de 50 caractères, vous le retournez non transformé. S'il en fait plus, vous le retournez tronqué, suivi du caractère    .

Pour utiliser un Pipe dans l'application, il faut le déclarer. Ajoutez-le aux déclarations de SharedModule, ainsi qu'aux exports pour le rendre disponible à toute l'application :

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { CommentsComponent } from './components/comments/comments.component';
import { MaterialModule } from './material.module';
import { ReactiveFormsModule } from '@angular/forms';
import { ShortenPipe } from './pipes/shorten.pipe';

@NgModule({
  declarations: [
    CommentsComponent,
    ShortenPipe
  ],
  imports: [
    CommonModule,
    MaterialModule,
    ReactiveFormsModule
  ],
  exports: [
    CommentsComponent,
    MaterialModule,
    ReactiveFormsModule,
    ShortenPipe
  ]
})
export class SharedModule { }

Vous pouvez maintenant appliquer ce Pipe sur le content des Posts. Dans  post-list-item.component.html  :

<span class="post-content">{{ post.content | shorten }}</span>

Dans le DOM, ça donne :

Un aperçu de texte, suivi de …
Le texte tronqué

Aussi simple que ça ! Mais on peut l'améliorer !

Acceptez un argument

Pour rendre  ShortenPipe  encore plus utile, vous allez lui faire accepter un argument optionnel pour fixer le nombre maximal de caractères (plutôt que garder les 50 caractères codés en dur). Ça se fait en ajoutant un deuxième paramètre à  transform  :

transform(value: string, maxLength = 50): string {
    if (value.length <= maxLength) {
        return value;
    }
    return value.substring(0, maxLength) + '…';
}

Cela vous permet, dans vos templates, de choisir le nombre de caractères auquel vous limitez les textes :

<span class="post-content">{{ post.content | shorten: 100 }}</span>

Allez, passons à un Pipe qui accepte autre chose qu'une chaîne de caractères comme paramètre.

Nommez vos utilisateurs

À vous de jouer !

À vous de jouer

Je vous propose d'essayer d'implémenter vous-même ce prochain Pipe qui :

  • acceptera un objet de type  { firstName: string, lastName: string }  ;

  • retournera    "LASTNAME Firstname"  . 

Par exemple, l'objet  { lastName: 'Alexander', firstName: 'Will' }  doit retourner  'ALEXANDER Will'  . Vous pourrez essayer votre Pipe en créant un objet temporaire dans l'un de vos components actuels.

Faites l'essai, et je vous propose ma solution ci-dessous.

Suivez ma solution

Voici ma proposition :

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'username'
})
export class UsernamePipe implements PipeTransform {
  transform(value: { firstName: string, lastName: string }): string {
    return `${value.lastName.toUpperCase()} ${value.firstName}`;
  }
}

Besoin de rien de plus ! Si vous déclarez ce Pipe et que vous l'utilisez dans l'un de vos templates avec un objet User temporaire, ça fonctionne tout seul !

Voici une idée pour une amélioration de  UsernamePipe  :

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'username'
})
export class UsernamePipe implements PipeTransform {
  transform(value: { firstName: string, lastName: string }, locale: 'en' | 'fr' = 'fr'): string {
    return locale === 'fr' ?
      `${value.lastName.toUpperCase()} ${value.firstName}` :
      `${value.firstName} ${value.lastName}`;
  }
}

Ici, je permets au développeur de choisir de localiser l'affichage du nom :

  • soit en  'fr'  (qui est également la valeur par défaut), en gardant le nom en majuscules suivi du prénom ;

  • soit en  'en'  , avec le prénom suivi du nom.

Allez, un dernier Pipe qui demande un peu plus d'une ligne de code, et qui crée un comportement que plusieurs de mes clients m'ont demandé !

Comptez les secondes, les heures, les jours

L'idée ici est d'afficher un texte descriptif de type "il y a quelques minutes" à la place des dates exactes.

Dans votre application, les dates  createdDate  sont sous forme de string, mais pour que ce Pipe soit le plus réutilisable possible, vous allez prendre en compte les deux possibilités :  string  et  Date  .

Voici l'implémentation que je vous propose :

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'timeAgo'
})
export class TimeAgoPipe implements PipeTransform {
  timeDiffs = {
    minute: 60 * 1000,
    hour: 60 * 60 * 1000,
    day: 24 * 60 * 60 * 1000,
    week: 7 * 24 * 60 * 60 * 1000,
    month: 30 * 24 * 60 * 60 * 1000,
    year: 365 * 24 * 60 * 60 * 1000
  };

  transform(value: string | Date): any {
    const now = Date.now();
    const then = new Date(value).getTime();
    const diff = now - then;
    if (diff < this.timeDiffs.minute) {
      return 'Il y a quelques secondes';
    } else if (diff < this.timeDiffs.hour) {
      return 'Il y a quelques minutes';
    } else if (diff < this.timeDiffs.day) {
      return 'Il y a quelques heures';
    } else if (diff < this.timeDiffs.week) {
      return 'Il y a quelques jours';
    } else if (diff < this.timeDiffs.month) {
      return 'Il y a quelques semaines';
    } else if (diff < this.timeDiffs.year) {
      return 'Il y a quelques mois';
    } else {
      return 'Il y a plus d\'un an';
    }
  }
}

Dans ce Pipe :

  • l'objet  timeDiffs  permet de regrouper (et de nommer) des durées en millisecondes pour la comparaison de deux dates ;

  • la méthode  transform  accepte des  string  ou des  Date  ;

  • une première constante  now  contient le moment exact du runtime sous forme du nombre de millisecondes depuis le 1er janvier 1970 (l'époque UNIX) ;

  • une deuxième constante  then  crée un nouvel objet  Date  à partir de value – ça marche pour les types  string  et  Date  – et utilise  getTime()  pour récupérer le nombre de millisecondes correspondant ;

  • une dernière constante  diff  correspond à la différence entre  now  et  then  .

On compare la valeur de  diff  aux différentes durées dans  timeDiffs  pour retourner un texte descriptif approprié.

Déclarez et exportez  TimeAgoPipe  dans SharedModule, et remplacez  DatePipe  dans PostListItemComponent ainsi que dans CommentsComponent, et vous aurez :

Des dates un peu floues
Des dates un peu floues

Implémenter ce genre de comportement serait possible sans Pipe, mais j'espère vous avoir convaincu que lorsque vous devez transformer une valeur pour l'afficher dans un template, l'utilisation d'un Pipe est généralement le meilleur choix architectural !

En résumé

  • On déclare un Pipe avec le décorateur  @Pipe  en y passant le nom que l'on souhaite utiliser.

  • La classe du Pipe implémente l'interface PipeTransform avec une méthode  transform  qui accepte au moins un paramètre, et le retourne transformé.

  • Passer un deuxième argument à  transform  permet d'implémenter une configuration depuis le template ; ex. :   {{ myText | shorten: 100 }}  ;

  • Les Pipes peuvent accepter plusieurs types en paramètre, y compris des objets complexes.

Maintenant que vous savez implémenter vos propres Pipes, suivez-moi au prochain chapitre pour apprendre comment créer des comportements dynamiques avec vos propres Directives ! 

Exemple de certificat de réussite
Exemple de certificat de réussite