loading.service.ts
import { Injectable } from '@angular/core'; import { RouteConfigLoadEnd, RouteConfigLoadStart, Router } from '@angular/router'; import { Observable } from 'rxjs'; import { filter, map } from 'rxjs/operators'; /** * Detect and inform if something is loading */ @Injectable({ providedIn: 'root' }) export class LoadingService { public readonly loading$: Observable<boolean>; constructor(router: Router) { this.loading$ = router.events.pipe( filter((event) => event instanceof RouteConfigLoadStart || event instanceof RouteConfigLoadEnd), map((event) => event instanceof RouteConfigLoadStart) ); } }
app.component.css
.loadbar.active { height: 2px; animation: loadbar-progress 3s forwards cubic-bezier(0, 1, 1, 1); } .loadbar.finish { animation: loadbar-finish 0.3s; } @keyframes loadbar-progress { from { width: 0; } to { width: 90%; } } @keyframes loadbar-finish { from { width: 90%; } to { width: 100%; } }
app.component.ts
export class AppComponent { public readonly loading$?: Observable<boolean>; public readonly finish$?: Observable<boolean>; constructor(@Inject(PLATFORM_ID) platform: object, { loading$ }: LoadingService) { if (isPlatformBrowser(platform)) { // Delays the loading indicator when turning to false to let the .finish class take effect this.loading$ = loading$.pipe(delayWhen((v) => interval(v ? 0 : 450))); // Inverts the loading indicator, the .finish class is used to rapidly push the loading indicator to 100% this.finish$ = loading$.pipe(map((v) => !v)); } } }
app.component.html
<div class="absolute bg-blue-500 loadbar z-50 w-full top-0 opacity-50" [ngClass]="{ finish: finish$ | async, active: loading$ | async }" ></div>