🌀 Un loader UX-friendly en Angular avec RxJS
Afficher un loader trop rapidement peut provoquer un effet de “flash” désagréable. À l’inverse, le masquer trop tôt peut perturber l’utilisateur. Voici une solution simple et réutilisable avec RxJS pour un loader intelligent et fluide.
Lien stackblitz.
🎯 Objectif
- Ne pas afficher le loader avant 200 ms, pour éviter les flashs inutiles.
- Une fois qu’il est affiché, le maintenir visible au moins 2 secondes, pour assurer une expérience utilisateur stable.
- Masquer le loader uniquement quand l’API a terminé de charger et que les 2 secondes sont écoulées.
🔧 Le code
// Timer déclenché après 200ms
const _passed200ms = timer(200).pipe(
map(() => true),
startWith(false)
);
// Timer déclenché après 2 secondes
const _passed2sDisplay = timer(2000).pipe(
map(() => true),
startWith(false)
);
// Fonction principale : retourne un Observable à consommer dans le template
function uiLoading$(isLoading$: Observable<boolean>) {
return combineLatest([_passed200ms, _passed2sDisplay, isLoading$]).pipe(
tap((data) => console.log('data', data)),
map(([passed200ms, passed2sDisplay, isApiStillLoading]) => {
map(([passed200ms, passed2sDisplay, isApiStillLoading]) => {
if (!passed200ms) {
return false;
}
// After 200ms: if still loading, show loader
if (isApiStillLoading === true) {
return true;
}
// API finished loading: only hide loader if 2s minimum display time passed
if (isApiStillLoading === false && passed2sDisplay === true) {
return false;
}
return false;
}),
}),
distinctUntilChanged()
);
}
🧪 Cas d’usage
🔹 Cas 1 : API lente → loader visible de 200ms à 5s
uiLoading$(
of('data').pipe(
delay(5000),
map(() => false),
startWith(true)
)
).subscribe(result => console.log('result', result));
🔹 Cas 2 : API très rapide → le loader ne s’affiche jamais
uiLoading$(
of('data').pipe(
delay(50),
map(() => false),
startWith(true)
)
).subscribe(result => console.log('result', result));
🔹 Cas 3 : API rapide mais pas instantanée → loader visible entre 200ms et 2s
uiLoading$(
of('data').pipe(
delay(1500),
map(() => false),
startWith(true)
)
).subscribe(result => console.log('result', result));
✅ Avantages
- 100 % réactif, sans variable d’état local ou setTimeout.
- Composable : peut être utilisé dans n’importe quel composant Angular.
- UX fluide : améliore la perception du chargement côté utilisateur.
Conseil perso: Fait en sorte de souscrire à cet observable directement dans ton template, cela va permettre au loader de s’adapter en conséquence.
Je m’excuse, je partage rapidement cet utilitaire, sans implémentation concrète. Toutefois, si tu as des questions ou si tu souhaites un example d’implémentation n’hésite pas.
Si l’article t’a plu ou si tu as des questions, n’hésite pas à laisser un commentaire ou encore à me suivre sur linkedin Romain Geffrault.