Appearance
Créer un loader global avec CDK Overlay
Dans ce tutoriel, vous allez apprendre à afficher un loader plein écran grâce à Angular CDK Overlay. L'objectif est d'améliorer l'expérience utilisateur pendant les changements de page ou les chargements plus longs.
Imaginez une caisse au supermarché : quand l'employé scanne vos articles, vous voyez que ça travaille. Un loader global joue le même rôle dans votre application : il indique que quelque chose se passe, au bon moment.
Pourquoi utiliser CDK Overlay ?
CDK Overlay permet d'afficher n'importe quel composant au-dessus de l'interface, au bon endroit, avec un backdrop et une gestion propre de l'empilement. C'est la boîte à outils idéale pour créer vos propres modales, toasts, popovers, menus contextuels, panneaux latéraux, ou loaders globaux, sans imposer de style.
Bon réflexe
Un loader global est utile quand une action bloque la navigation. Évitez de l'afficher pour des micro-actions rapides.
Installation d'Angular CDK
Commençons par installer Angular CDK :
bash
ng add @angular/cdkQu'est-ce que Angular CDK ?
Angular CDK est un ensemble d'outils de développement qui fournit des composants réutilisables et des services pour créer des fonctionnalités complexes dans les applications Angular. Il offre des solutions pour la gestion des overlays, du drag & drop, des tables virtuelles, de l'accessibilité et bien d'autres fonctionnalités, sans imposer de style visuel spécifique. C'est la base sur laquelle Angular Material est construit, mais il peut être utilisé indépendamment pour créer vos propres composants personnalisés.
1) Créer le composant Loader
On crée un composant autonome qui affiche un spinner et un texte.
ts
import { Component } from '@angular/core';
@Component({
selector: 'app-loader',
standalone: true,
template: `
<div class="loader-container">
<div class="spinner"></div>
<p class="loader-text">Loading...</p>
</div>
`,
styles: [
`
.loader-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 1rem;
padding: 2rem;
background: white;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.spinner {
width: 48px;
height: 48px;
border: 4px solid #f3f3f3;
border-top: 4px solid #3498db;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.loader-text {
margin: 0;
color: #333;
font-size: 0.875rem;
font-weight: 500;
}
`
]
})
export class Loader {}2) Créer un LoaderService basé sur Overlay
Ce service écoute les événements du Router et affiche un overlay pendant la navigation.
ts
import { Overlay, OverlayRef } from '@angular/cdk/overlay';
import { ComponentPortal } from '@angular/cdk/portal';
import { inject, Injectable } from '@angular/core';
import {
Event,
NavigationCancel,
NavigationEnd,
NavigationError,
NavigationStart,
Router
} from '@angular/router';
import { filter } from 'rxjs';
import { Loader } from './loader';
@Injectable({
providedIn: 'root'
})
export class LoaderService {
private router = inject(Router);
private overlay = inject(Overlay);
private overlayRef: OverlayRef | null = null;
initialize(): void {
const endNavigation = (event: Event): boolean => {
return (
event instanceof NavigationEnd ||
event instanceof NavigationCancel ||
event instanceof NavigationError
);
};
this.router.events
.pipe(
filter((event) => {
return event instanceof NavigationStart || endNavigation(event as Event);
})
)
.subscribe((event) => {
if (event instanceof NavigationStart) {
this.createOverlay();
return;
}
this.destroyOverlay();
});
}
private createOverlay(): void {
if (this.overlayRef) {
return;
}
this.overlayRef = this.overlay.create({
hasBackdrop: true,
scrollStrategy: this.overlay.scrollStrategies.block(),
positionStrategy: this.overlay
.position()
.global()
.centerHorizontally()
.centerVertically()
});
this.overlayRef.attach(new ComponentPortal(Loader));
}
private destroyOverlay(): void {
if (!this.overlayRef) {
return;
}
this.overlayRef.detach();
this.overlayRef.dispose();
this.overlayRef = null;
}
}3) Comprendre comment fonctionne un Overlay
Un overlay est une couche flottante, indépendante de vos composants et de leurs templates. Il vit dans un conteneur dédié géré par le CDK, au-dessus de l'application. Cette couche est contrôlée par un objet OverlayRef.
Pour créer cette couche, on passe une configuration claire au service Overlay :
ts
this.overlayRef = this.overlay.create({
hasBackdrop: true,
scrollStrategy: this.overlay.scrollStrategies.block(),
positionStrategy: this.overlay
.position()
.global()
.centerHorizontally()
.centerVertically()
});Voici ce que fait chaque option :
hasBackdrop: trueajoute un fond sombre qui empêche les clics sur le contenu derrière.scrollStrategy: block()bloque le scroll pendant que le loader est visible.positionStrategy: global().centerHorizontally().centerVertically()place l'overlay au centre de l'écran.
Le résultat de create() est un OverlayRef. C'est votre poignée pour :
- attacher du contenu,
- détacher/fermer l'overlay,
- libérer la mémoire avec
dispose().
Et pour afficher le loader dans l'overlay :
ts
this.overlayRef.attach(new ComponentPortal(Loader));ComponentPortal est un conteneur qui instancie un composant Angular hors du template classique. Il sert de pont entre Angular et l'overlay. Sans portal, l'overlay n'a rien à afficher.
Plus concrètement :
new ComponentPortal(Loader)crée une “capsule” qui sait instancier le composantLoader.overlayRef.attach(...)branche cette capsule dans la couche flottante.
Autrement dit, cette ligne dit : “Montre ce composant dans la couche overlay.”
CDK Overlay fonctionne toujours en deux étapes :
- Créer l'overlay (
overlay.create(...)). - Attacher un portal (
overlayRef.attach(...)).
Bon réflexe
Toujours dispose() l'overlay pour libérer les ressources et éviter des overlays fantômes.
4) Démarrer le loader au lancement de l'app
On initialise le service au boot avec provideAppInitializer. On ajoute aussi OverlayModule pour fournir le service Overlay.
ts
import { OverlayModule } from '@angular/cdk/overlay';
import {
ApplicationConfig,
importProvidersFrom,
inject,
provideAppInitializer
} from '@angular/core';
import { provideRouter } from '@angular/router';
import { LoaderService } from './loader.service';
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
importProvidersFrom(OverlayModule),
provideAppInitializer(() => {
const loaderService = inject(LoaderService);
loaderService.initialize();
})
]
};Astuce
Vous pouvez personnaliser le backdrop avec backdropClass pour assombrir ou flouter l'arrière-plan.
Aller plus loin avec Overlay
CDK Overlay est bien plus qu'un loader. Voici ce que vous pouvez construire :
- Menus contextuels qui s'ancrent à un bouton ou à la souris.
- Popovers ou tooltips avec positionnement automatique.
- Panneaux latéraux (side panels) qui glissent depuis un bord.
- Toasts en pile (avec une stratégie d'empilement).
- Modales légères personnalisées (sans Angular Material).
Les briques principales à connaître :
- Position strategies :
global()pour centrer,flexibleConnectedTo()pour s'ancrer à un élément. - Backdrop :
hasBackdrop,backdropClass, fermeture au clic si besoin. - Scroll strategies :
block,noop,repositionouclose. - Stacking :
OverlayRefpermet de gérer plusieurs overlays propres et indépendants.
Quand utiliser Overlay plutôt qu'un composant classique ?
Dès que vous devez sortir du flux du DOM (z-index, position absolue, clipping), l'Overlay est la bonne solution.
Playground complet
Ce playground montre un loader qui s'affiche à chaque navigation.
Résumé
Vous avez maintenant un loader global qui s'affiche automatiquement pendant les navigations grâce à CDK Overlay. C'est une base solide pour améliorer la perception de vitesse et la clarté de votre interface.
Si vous voulez aller plus loin, vous pouvez :
- ajouter un message dynamique dans le loader,
- désactiver le backdrop,
- afficher le loader pendant des requêtes HTTP spécifiques.
