Pour fournir une expérience moderne et intuitive à vos utilisateurs, les animations sont un outil indispensable. Il y a plein de "moments" où une animation bien conçue facilite l'utilisation de votre application :
du feedback visuel pour valider une action de l'utilisateur ;
montrer un chargement ou une progression ;
attirer l'attention de l'utilisateur sur la prochaine étape d'une suite ;
lisser des transitions, ou des arrivées/départs d'éléments de l'écran.
Et encore bien d'autres !
Les animations CSS sont puissantes, mais ne peuvent pas tout faire – pas facilement, du moins ! Si, par exemple, une animation dépend d'une valeur dans votre component, ou si elle doit être déclenchée par un autre événement, ça devient de plus en plus difficile à gérer en CSS pur !
Les animations Angular sont un outil qui permet de répondre à ces besoins, comme vous le découvrirez au cours des prochains chapitres.
Commençons d'abord par créer une animation !
Dans ce cours, BrowserAnimationsModule a été importé dans AppModule par Material. Profitons-en pour le déplacer dans les imports de CoreModule (car il s'agit d'un module importé une seule fois pour toute l'application) :
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HeaderComponent } from './components/header/header.component';
import { SharedModule } from '../shared/shared.module';
import { RouterModule } from '@angular/router';
import { HttpClientModule } from '@angular/common/http';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
@NgModule({
declarations: [
HeaderComponent
],
imports: [
CommonModule,
SharedModule,
RouterModule,
HttpClientModule,
BrowserAnimationsModule
],
exports: [
HeaderComponent
]
})
export class CoreModule { }Je vous propose d'ajouter des animations aux commentaires des utilisateurs dans CommentsComponent.
Pour ajouter des animations dans un component, ça se passe dans l'objet de configuration que vous passez à son décorateur @Component . Ajoutez un tableau animations à CommentsComponent :
// ...
import { trigger } from '@angular/animations';
@Component({
selector: 'app-comments',
templateUrl: './comments.component.html',
styleUrls: ['./comments.component.scss'],
animations: [
trigger('listItem', [
])
]
})
export class CommentsComponent implements OnInit {
// ...
}Le trigger que vous ajoutez ici est ce qui permet d'attribuer une ou plusieurs animations aux éléments HTML dans votre template avec la syntaxe @ :
<!-- ... -->
<h2 matSubheader>Commentaires</h2>
<mat-list-item *ngFor="let comment of comments" @listItem>
<!-- ... -->Bien sûr, dans l'application, il ne se passe rien encore, car vous n'avez pas encore ajouté d'animation ! Changeons ça tout de suite !
Le plus souvent, les animations que vous ajoutez à vos applications sont des animations de transition entre différents états.
Ce sera le cas ici lorsque vous voudrez animer les commentaires des utilisateurs quand la souris passe dessus : vous animez la transition entre un état "par défaut" et un état "actif" ou "survolé".
Les animations Angular de ce type sont définies avec state() , style() , transition() , et animate() :
trigger('listItem', [
state('default', style({
transform: 'scale(1)',
'background-color': 'white',
'z-index': 1
})),
state('active', style({
transform: 'scale(1.05)',
'background-color': 'rgb(201, 157, 242)',
'z-index': 2
})),
transition('default => active', [
animate('100ms ease-in-out')
]),
transition('active => default', [
animate('500ms ease-in-out')
]),
])Dans cet extrait :
vous définissez un état default comme état par défaut avec state , en lui passant le nom de l'état et un appel à style . À style , vous passez les styles qui correspondent à cet état : il est essentiel de "réinitialiser" les styles qui seront modifiés lors du changement d'état ;
vous définissez l'état lorsque la souris passera sur l'élément, avec le nom active et les styles correspondants ;
vous configurez les transitions dans les deux sens en décrivant la transition à configurer et en y attribuant un tableau contenant la définition de l'animation avec animate – de default à active, ça ira beaucoup plus vite que de active à default.
Tout est prêt, mais votre trigger ne sait pas encore quand ni comment passer d'un état à l'autre !
Pour assigner un state à un trigger dans le template, vous allez utiliser l'attribute binding, avec une variable côté TypeScript qui contiendra soit 'default' , soit 'active' :
Dans le TypeScript :
listItemAnimationState: 'default' | 'active' = 'default';Dans le template :
<mat-list-item *ngFor="let comment of comments" [@listItem]="listItemAnimationState">Du coup, il suffit de modifier la valeur de listItemAnimationState lorsque la souris entre et sort des MatListItem :
Dans le TypeScript :
onListItemMouseEnter() {
this.listItemAnimationState = 'active';
}
onListItemMouseLeave() {
this.listItemAnimationState = 'default';
}Et dans le template :
<mat-list-item *ngFor="let comment of comments"
[@listItem]="listItemAnimationState"
(mouseenter)="onListItemMouseEnter()"
(mouseleave)="onListItemMouseLeave()">On a un seul problème : l'animation a bien lieu, mais sur tous les commentaires à la fois !
Mais c'est normal ! On n'a pas créé de système permettant au component d'assigner des états différents aux différents MatListItem !

Je vous propose un challenge un peu costaud, pour lequel il y a énormément de solutions possibles !
Il faut trouver une façon de gérer les états différents de tous les commentaires dans chaque instance de CommentsComponent.
Chaque itération (c'est un indice !) de <mat-list-item> devra pouvoir lire et modifier son état via l'attribute binding du trigger et les méthodes.
Il y a plein d'approches possibles : ci-dessous, je vous donne la mienne. Essayez par vous-même, puis suivez-moi !
Je vous propose de créer un dictionnaire qui associe l'index du commentaire dans le tableau comments à son état :
animationStates: { [key: number]: 'default' | 'active' } = {};Comme ça, le trigger du template peut récupérer son état grâce à l' index mis à disposition par *ngFor :
<mat-list-item *ngFor="let comment of comments; let i = index"
[@listItem]="animationStates[i]">Puisque cet objet est vide par défaut, il faut le peupler dans ngOnInit :
for (let index in this.comments) {
this.animationStates[index] = 'default';
}Pour chaque élément du tableau comments , vous associez 'default' à son index dans l'objet animationStates . Par exemple, s'il y a trois commentaires, animationStates finira comme ça :
animationStates = {
0: 'default',
1: 'default',
2: 'default'
};Il ne reste plus qu'à modifier les empreintes et implémentations de vos deux méthodes d'événement pour qu'elles interagissent avec le dictionnaire :
onListItemMouseEnter(index: number) {
this.animationStates[index] = 'active';
}
onListItemMouseLeave(index: number) {
this.animationStates[index] = 'default';
}Et leur passer l'index du commentaire depuis le template :
<mat-list-item *ngFor="let comment of comments; let i = index"
[@listItem]="animationStates[i]"
(mouseenter)="onListItemMouseEnter(i)"
(mouseleave)="onListItemMouseLeave(i)"
>Avec ça, vos commentaires s'animent correctement !

Les animations d'un component s'enregistrent au niveau du décorateur @Component .
On utilise trigger pour définir un regroupement d'états et de transitions à assigner aux différents éléments.
Les différents états d'un élément animé se définissent avec state et les styles passés à style .
Définir les transitions entre états se fait avec transition et animate .
On assigne un trigger à un élément du template avec @ , par exemple @fadeIn .
On peut assigner le state d'un trigger avec l'attribute binding, par exemple [@fadeIn]="faded" .
Dans le chapitre suivant, vous découvrirez un autre type de transition – vous êtes prêt ?