Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle angular2 lazy loading route failure

I'm using angular 2 lazy routing. Client is bundled with webpack2 using AOT and angular-router-loader to lazy load children. Everything works as expected when the browser is connected, that is I can successfully navigate the router to the lazy loaded module, the chunk is loaded successfully and I can view the component etc.

However if I emulate being disconnected (e.g by using the Offline chrome developer tools option) routing fails, as expected, because it can't load the relevant chunk. The error is 'Loading chunk [chunk number] failed'

After that no routing command works, it's like the router is broken.

I tried to handle the error using a global ErrorHandler. The idea was that perhaps I could catch the error before it breaks the router, but it seems that it is too late by then. At the time I catch the error the router is not working.

import { Injectable, ErrorHandler, Injector } from '@angular/core';

import { Router } from '@angular/router';

@Injectable()
export class CustomErrorHandler extends ErrorHandler {
    constructor(private injector: Injector) {
        super(false);
    }

    private get router(): Router {
        return this.injector.get(Router, null);
    }

    public handleError(error: Error) {
        if (/Loading chunk [\d]+ failed/.test(error.message)) {
            console.info('Offline mode');
            let router = this.router;
            if (router) {
                router.navigateByUrl('/offline').then(t => console.debug(t+'')).catch(t=> console.debug(t));
            } else {
                window.location.href = '/';
            }
        }
    }
}

The Custom error handler works because the 'Offline mode' message is printed. Injection also works, router is not null, however router navigation does not work, the router promise is not resolved neither rejected.

What I'm trying to accomplish is to handle the error (e.g. to show an informative message to the user) and at the same time having the router at a working state, so that the user can navigate later (when internet connection is restored) without reloading the whole page.

Update 1: trying to reproduce without aot and webpack

In order to see if this is an angular router issue I tried to see what happens when trying to work offline with jit compilation. I used the : angular router plunker navigated to login tab, switched to offline and pressed login. A number of 'Error: XHR error loading' errors were logged as the client was trying to load the admin module. Ultimately navigation failed, however routing navigation did not break after that. I was able to navigate to other tabs and after switching back online I was even able to navigate to admin. Perhaps the problem is how angular-router-loader webpack plugin tries to load the module.

Update 2: Seems to be a known issue

Feature: more robust module loader for router

like image 947
angeor Avatar asked Dec 15 '16 13:12

angeor


People also ask

What is lazy loading in angular2?

Lazy loading modules helps us decrease the startup time. With lazy loading our application does not need to load everything at once, it only needs to load what the user expects to see when the app first loads. Modules that are lazily loaded will only be loaded when the user navigates to their routes.

How do you check lazy loading is working or not?

If you're not sure if lazy loading is working correctly, you can open the Chrome DevTools and check that the images are not loaded until the scroll.

How do you implement lazy loading?

To lazy load an image, display a lightweight placeholder image, and replace with the real full-size image on scroll. There are several technical approaches to lazy loading images: Inline <img> tags, using JavaScript to populate the tag if image is in viewport. Event handlers such as scroll or resize.

What is lazy loading in angular10?

Lazy loading (also called on-demand loading) is an optimisation technique for the online content, be it a website or a web app.


1 Answers

Prevent the routing failure rather than handling it.

You could preload your lazy loaded modules so they are loaded to the client once the browser detects inactivity, that way they would be available in offline mode.


Check here to preload all modules: PreloadAllModules


You could also create your custom loading strategy and preload only selected modules:

custom-preloading.ts
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/mergeMap';
import { PreloadingStrategy, Route } from '@angular/router';

export class CustomPreloading implements PreloadingStrategy {

   public preload(route: Route, fn: () => Observable<boolean>): Observable<boolean> {
     if (route.data && route.data.preload) {
       return Observable.of(true).flatMap((_: boolean) => fn());
     } else {
       return Observable.of(false);
     }
   }
}
app-routing.module.ts
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { CustomPreloading } from './custom-preloading';

const routes: Routes = [
  { ..other paths.. },
  { path: 'lazy', loadChildren: 'app/lazy/lazy.module#LazyModule', data: { preload: true }  }
];

export const appRoutingProviders = [
   CustomPreloading
];

@NgModule({
   imports: [RouterModule.forRoot(routes, { preloadingStrategy: CustomPreloading })],
   exports: [RouterModule]
})
export class AppRoutingModule { }
like image 65
Davíð Már Gunnarsson Avatar answered Oct 04 '22 10:10

Davíð Már Gunnarsson