Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Show loading spinner in lazy-loading modules

Tags:

css

angular

I have implemented a splash screen in my app, as this way:

index.html:

<app-root></app-root>
<div class="splash spinner"></div>

css:

// ... Styles about spinner
app-root:empty + .splash {
    opacity: 1;
}

Ok, in this case, the app-root when it's empty, I have a spinner animation and there is no problem, it works fine.

But, my problem comes now, I have lazy loading in routing:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { AuthGuard } from './core';

import {
  ...
} from './auth';

export const routes: Routes = [
  {
    ..
  },
  {
    path: 'api',
    loadChildren: 'app/api/myapi.module#MyApiModule',
    canActivate: [ AuthGuard ],
  },
  },
];

@NgModule({
  imports: [
    RouterModule.forRoot(routes, {
      enableTracing: true
    }),
  ],
  exports: [ RouterModule ]
})
export class AppRoutingModule { }

In /apis path I'm lazy loading MyApiModule, and inside MyApiModule, I have another routing:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { MyComponent } from './mycomponent.component';
import { OtherComponent } from './othercomponent.component';

const routes: Routes = [
  {
    path: '',
    component: MyComponent,
    children: [
      {
        path: '',
        component: OtherComponent,
      },
      {
        path: ':id/api',
        loadChildren: 'app/api/api2/api.module#ApiModule',
      },
    ],
  },
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class OtherRoutingModule { }

I want to show the same splash screen as I set in 'app-root' when my app is loading a 'lazy-loaded' module (the ApiModule module).

The goal is to show splash screen when a lazy module is loading so, my question is, is there any 'easy' way to catch when a load-children module is loaded (by events or something)? Or I need to accomplish the same way as I did (check in css the router-outlet... etc etc).

like image 438
Kalamarico Avatar asked Mar 13 '26 18:03

Kalamarico


1 Answers

app-root:empty selector is more of a hack rather than universal solution for loading indicator. This or similar selector can still be additionally used for initially rendered page when scripts aren't loaded yet.

Loading indicator may be shown in some common scenarios:

  • Asynchronous module-level lazy loading (SystemJS, Webpack chunk loading, etc)
  • long synchronous application-level actions
  • asynchronous application-level actions
    • HTTP requests (Http, HttpClient, native XHR, ...)
    • IndexedDB requests
    • web worker interoperation

All of these scenarios can occur independently (e.g. triggered by components) or be applicable to Angular application phases that may cause visible delays:

  • initial application initialization
  • route change

It always depends on the case whether a spinner should be triggered at lower (scenarios) or higher (phases) level.

Some scenarios can be conventionally handled (HttpClient requests can be tracked with interceptors).

Some scenarios cannot be efficiently handled. This includes ES and Angular lazy-loaded modules, since they are handled at low level and usually don't expose a promise to chain.

So lazy-loaded Angular modules leave the only option to trigger a spinner, i.e. on route change:

router.events.subscribe(e => {
    if (e instanceof NavigationStart) {
        // show spinner
    } elseif (e instanceof NavigationEnd {
        || e instanceof NavigationError
        || e instanceof NavigationCancel) {
        // hide spinner
    }
});

If some of asynchronous scenarios occur outside of route resolvers, HttpClient interceptors can be additionally involved.

Since several processes that trigger a spinner may occur simultaneously, a spinner should be implemented with a counter and not boolean flag, like is shown here.

like image 170
Estus Flask Avatar answered Mar 16 '26 08:03

Estus Flask



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!