Preloading

Strategies for Angular Modules

Angular RTP Meetup

Hey, Am Udhay (OO-dhy)

Agenda

  • Single Module Angular App
  • Multi Module Angular App
  • Lazy loading
  • Preloading Modules:
    • All
    • Selective
    • Network based
    • Quick link
  • Discussion (Q/A)

 

Single Module Angular App

  • App contains only one module that has Components, Pipes, Directives and Services.
  • It works great for small web application with five to ten pages. 

Module

Components

Pipes

Directives

Multi Module Angular App

  • App contains more than one module with App module being the root module. Each modules contains Components, Pipes, Directives and Services.
  • It works great for Enterprise level applications

Root / Main

Home

Products

Cart

Payment

Orders

Lazy loading

Lazy loading

  • Loads Angular Module only when the route is visited / accessed.

Root / Main

Home

Products

Cart

Payment

Orders

/home

/products

/cart

/billing

/orders

https://someonlinestore.com/

Lazy loading (Contd)

Create new Angular project

ng new someonlinestore --routing
ng generate module payment --route billing --module app.module

Create feature module (Angular takes care of lazy loading configuraiton)

Lazy loading (Contd)

Application's Root Module Routes look like:

const routes: Routes = [
  {
    path: 'billing',
    loadChildren: () => 
    	import('./payment/payment.module').
        		then((m) => m.PaymentModule),
  }
];

When User accesses https://someonlinestore.com/billing, Payment module gets loaded and their content painted on UI

Drawback of Lazy Loading

  • If the bundle (Feature module) size is huge, it makes User to wait couple moments to load the page when the respective route is accessed.

This can be fixed by Preloading the module that's huge in size based on different strategies. Let's dig into it more..

Preloading Strategy - All

Preload All Modules by setting preloadingStrategy router option as below:

@NgModule({
  imports: [
    RouterModule.forRoot(routes, 
        { preloadingStrategy: PreloadAllModules }
     ),
  ],
  exports: [RouterModule],
})

Preloading Strategy - Selective

Set Preload JSON parameter value to either true or false in the route. And update preloadingStrategy to SelectivePreloadStrategy (let's create it).

const routes: Routes = [  
  {
    path: 'home',
    loadChildren: () => import('./home/home.module').then((m) => m.HomeModule),
    data: {preload: true}
  },
  {
    path: 'about',
    loadChildren: () =>
      import('./about/about.module').then((m) => m.AboutModule),
    data: {preload: false} 
  }, 
];

@NgModule({
  imports: [
    RouterModule.forRoot(routes, {preloadingStrategy: SelectivePreloadStrategy}),
  ],
  exports: [RouterModule],
})

Preloading Strategy - Selective (Contd)

Create a Service class 'SelectivePreloadStrategy' & implement PreloadingStrategy Interface. Add logic within preload method that decides whether to preload module or not..

import { Injectable } from '@angular/core';
import { PreloadingStrategy, Route } from '@angular/router';
import { Observable, EMPTY } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class SelectivePreloadStrategy implements PreloadingStrategy{

  constructor() { }

  preload(route: Route, load: () => Observable<any>): Observable<any>{
    return route.data && route.data['preload'] ? load() : EMPTY;
  }

}
const routes: Routes = [  
  {
    path: 'home',
    loadChildren: () => import('./home/home.module').then((m) => m.HomeModule),
    data: {preload: true}
  },
  {
    path: 'about',
    loadChildren: () =>
      import('./about/about.module').then((m) => m.AboutModule),
    data: {preload: false} 
  }, 
];

@NgModule({
  imports: [
    RouterModule.forRoot(routes, {preloadingStrategy: NetworkPreloadStrategy}),
  ],
  exports: [RouterModule],
})

Preloading Strategy - Network Connection Based

Update preloadingStrategy to NetworkPreloadStrategy (let's create it).

import { Injectable } from '@angular/core';
import { PreloadingStrategy } from '@angular/router';
import { EMPTY, TimeoutError } from 'rxjs';

export declare var navigator;

@Injectable({
  providedIn: 'root'
})
export class NetworkPreloadStrategy implements PreloadingStrategy{

  constructor() { }
  preload(route: import("@angular/router").Route, load: () => import("rxjs").Observable<any>): import("rxjs").Observable<any> {
    return this.hasGoodNetworkConn() ? load() : EMPTY;
  }

  hasGoodNetworkConn() {
    const conn = navigator.connection;
    if(conn){
      const connectionsToAvoid = ['2g', '3g'];
      const connectionType = conn.effectiveType || '';
      console.log(connectionType);
      if(connectionsToAvoid.includes(connectionType)){
          return false;
      }
    }   
    return true;
  }
}

Preloading Strategy - Network Connection Based (Contd)

Decide preloading based on Network..

Preloading Strategy - Quick Links Strategy

const routes: Routes = [  
  {
    path: 'home',
    loadChildren: () => import('./home/home.module').then((m) => m.HomeModule),
    data: {preload: true}
  },
  {
    path: 'about',
    loadChildren: () =>
      import('./about/about.module').then((m) => m.AboutModule),
    data: {preload: false} 
  }, 
];

@NgModule({
  imports: [
    RouterModule.forRoot(routes, {preloadingStrategy: QuickLinkStrategy}),
  ],
  exports: [RouterModule],
})

Using ngx-quicklink, the configuration is straightforward..

Import the QuicklinkModule in AppModule and add QuickLinkStrategy as Preloading strategy

Links

Check my previous talks -  http://askudhay.com/

ng Thanks!

Made with Slides.com