Show activity Indicator while loading a lazy loaded Module in Angular 2
Solution 1
You can listen for two router events:
RouteConfigLoadStart
RouteConfigLoadEnd
They fire when a lazy loaded module is being loaded. The advantage of using these over the standard router events such as NavigationStart
is that they won't fire on every route change.
Listen to them in your root AppComponent to show / hide your spinner.
app.component.ts
import { Router, RouteConfigLoadStart, RouteConfigLoadEnd } from '@angular/router';
...
export class AppComponent implements OnInit {
loadingRouteConfig: boolean;
constructor (private router: Router) {}
ngOnInit () {
this.router.events.subscribe(event => {
if (event instanceof RouteConfigLoadStart) {
this.loadingRouteConfig = true;
} else if (event instanceof RouteConfigLoadEnd) {
this.loadingRouteConfig = false;
}
});
}
}
app.component.html
Just a simple string here, but you could use a spinner component.
<router-outlet></router-outlet>
<ng-container *ngIf="loadingRouteConfig">Loading route config...</ng-container>
I'm using this approach with Angular v4.2.3
Solution 2
you can do it like this
- in app.component.html
<div class="main-loader" *ngIf="loading"> <div class="cssload-container" > <div class="cssload-whirlpool"></div> </div> </div>
-
in app.component.ts
import { Router, NavigationStart, NavigationEnd } from '@angular/router'; loading:boolean = false; constructor(private router:Router) { router.events.subscribe(event => { if(event instanceof NavigationStart) { this.loading = true; console.log("event started") }else if(event instanceof NavigationEnd) { this.loading = false; console.log("event end") } // NavigationEnd // NavigationCancel // NavigationError // RoutesRecognized }); }
in css any loading animation
hope this is useful to you. thanks
Solution 3
You can just use CSS !
<routler-outlet></routler-outlet>
<div class='.loader>
Just, wait a sec ! I'm loading
</div>
In your template
router-outlet + .loader {
opacity : 1;
}
.loader {
opacity : 0;
}
Then you can create fancy spinners with HTML/CSS
Solution 4
It's possible to click a link to other lazy-loaded route while loading first one. That's why we need to count routes being loaded:
app.component.ts
```
export class AppComponent {
currentlyLoadingCount = this._router.events.scan((c, e) => this._countLoads(c, e), 0);
constructor(private _router: Router) { }
private _countLoads(counter: number, event: any): number {
if (event instanceof RouteConfigLoadStart) return counter + 1;
if (event instanceof RouteConfigLoadEnd) return counter - 1;
return counter;
}
```
app.component.html
<ng-container *ngIf="currentlyLoadingCount | async">Loading route config...</ng-container>
vicmac
Updated on July 09, 2022Comments
-
vicmac almost 2 years
My scenario is as follows. I have a menu, with multiple options. Each menu should be shown depending on user permissions (already solved), most menu items are encapsulated as modules, and most of the modules are lazy loaded, so when a user clicks a menu item the first time, it loads (up to here everything works well), now my requirement is, in order to give a better user experience, I need to show activity indicator after user clicks a menu item while the lazy loaded module is loading.
Up to this, I tried using canActive, canLoad, canActivateChild interfaces from Angular Router but with no luck.
Any ideas?
-
vicmac about 7 yearscan you elaborate more your answer. I mean, I have a root module, and a feature module to manage all the menus, then when I click in a menu item, it loads a lazy loaded module, so while is loading I would like to show the activity indicator but once the lazy module is loaded, hide the activity indicator. I can't see how to apply this.
-
YounesM about 7 yearsI'm guessing you have a router-outlet to load your component. When the component is not loaded
<router-outlet>
will be empty. And that will trigger thecontainer:empty + .loader
selector and will display your loader. When the data is loaded. <router-outlet> will not be empty anymore, thencontainer:empty + .loader
selector will not be active anymore and your loader will go back to anopacity
of 0 -
vicmac about 7 yearsactually yes I have a router-outlet. In fact, I have a app-component with a router-outlet the root outlet and also I have another module which is in charge to manage all the menus, so it has another router-outlet. I just tried your answer putting the class container in my "child router outlet", but it is shown even after module has been loaded.
-
YounesM about 7 years@vimac Oops, my bad, I see what was wrong. I was assuming that component load IN router outlet but in fact they load AFTER the outlet. I edited my code. It should be fine now.
-
vicmac about 7 yearsI have a app component with a router-outlet, and an IndexComponent, to show a button to do the authentication by oauth, once the authentication is done user is redirected to IndexComponet again, but this time logged is true, so I detect that and redirects the user to the home page which is anther feature module with a MenuBarComponent to manage all menus and it's template is as follows, the styles are also here inside a style tag. <nav> ... menus goes here </nav> <router-outlet class='container'></router-outlet> <div class='.loader'> Just, wait a sec ! I'm loading </div>
-
YounesM about 7 years@vicmac Did you see my last edit ? It should work properly with that.
-
vicmac about 7 yearsyes. I tried it but unfortunatelly is not giving me the results that I want
-
vicmac over 6 yearsI've been out for a while, working on my project. I just resolved with a shared service which is triggered when some http request is started, it solves y problem in part, but I give it a try ASAP. Thanks.
-
Sangwin Gawande over 6 yearsYou don't need to subscribe twice. Like Daniel Crisp answer, you can do it once. It works and its better to subscribe once.
-
WillyC over 5 yearsThis works in that it shows the text, but if you use a spinner or something with CSS animations the animations stop once the module starts to load (although the animation component itself is shown, it is frozen on a frame). Note this isn't a problem with the spinner itself - only when a module is loading.
-
Flo over 5 yearsIs there any way to use CSS animations in this scenario?
-
Bart Calixto about 5 yearsyou are missing a
'
on<div class='.loader>
-
Cito almost 5 yearsIf you are using the PreloadAllModules strategy, then this would also show all modules loading in the background. In that case, it's best to also catch NavigationStart and NavigationEnd and ignore RouteConfigLoadStart and RouteConfigLoadEnd outside these events.
-
Uriy MerkUriy almost 4 yearsCan add
else if (event instanceof NavigationCancel) { this.isLoading = false }
for cases when there is a move to another route/module before the finish lazy loading. -
waternova almost 3 yearsIf you want a delay before showing the loading state (i.e. only show loading on slow loads), you can use a filter and a switchMap to only set this.loading = true after a delay. Inspired by stackoverflow.com/a/51998101/3370010