4

I have a complete angular app that uses eager loading. I want to convert it to lazy loading, but because I have guard on all of my routes and all of them are sub routes to one main route that is guarded, I don't know if it's possible to do it and still make it function like with eager loading.

This is my routing array in app-routing.module:

// Routing array - set routes to each html page
const appRoutes: Routes = [
  { path: 'login/:id', canActivate: [AuthGuard], children: [] },
  { path: '', canActivateChild: [AuthGuard], children: [
    { path: '', redirectTo: '/courses', pathMatch: 'full' },
    { path: 'courses', component: CourseListComponent,  pathMatch: 'full'},
    { path: 'courses/:courseId', component: CourseDetailComponent, pathMatch: 'full' },
    { path: 'courses/:courseId/unit/:unitId', component: CoursePlayComponent,
      children: [
        { path: '', component: CourseListComponent },
        { path: 'lesson/:lessonId', component: CourseLessonComponent, data:{ type: 'lesson'} },
        { path: 'quiz/:quizId', component: CourseQuizComponent, data: {type: 'quiz'} }
      ]}
    ]},
  { path: 'welcome', component: LandingPageComponent, pathMatch: 'full' },
  { path: '**', component: PageNotFoundComponent, pathMatch: 'full' }];

What I want to know is if it's possible to implement this with lazy loading and if so I'd like to know the main idea or what I need to know in order to do that.

In all the tutorials I did I never encounter this kind of thing. Thank you very much

Ofir Sasson
  • 673
  • 4
  • 16
  • 39
  • 1
    You need to look at the `canLoad` guard method. And then for example if you plan to move all `courses` as a lazy loaded module. Then create a routing-config for `courses` module and have a separate guard to manage its child paths – ashish.gd Mar 28 '19 at 11:22
  • where I need to put canLoad? on the app routing module? or in the courses module? If you can explain a little more I'd be thankful, I'm a little new to this lazy loading – Ofir Sasson Mar 28 '19 at 11:27
  • If you know where I can get an example that I can learn from I'd be great – Ofir Sasson Mar 28 '19 at 11:27
  • I tried to convert to lazy loading but I got many errors when I did ng build. I update the question and added the errors and the changes I did. – Ofir Sasson Apr 02 '19 at 08:16

4 Answers4

5

Thank you everyone for your answers. I managed to convert my routing to lazy loading successfully. This is the code:

app-routing.module

import { NgModule } from '@angular/core';
import { Routes, RouterModule, Router } from '@angular/router';
import { AuthGuard } from './auth.guard';

import { AppComponent } from './app.component';
import { PageNotFoundComponent } from './page-not-found/page-not-found.component';
import { LandingPageComponent } from './landing-page/landing-page.component';
import { HeaderComponent } from './header/header.component';
import { CourseModule } from './courses/course.module';


const routes:Routes = [
  { path: 'welcome', component: LandingPageComponent, pathMatch: 'full' },
  { path: 'login/:id', canActivate: [AuthGuard],  children: [] },
  { path: '', canActivateChild: [AuthGuard], children: [
    { path: '', redirectTo: 'courses', pathMatch: 'full' },
    { path: 'courses',  loadChildren: () => CourseModule }
  ]},
  { path: '**', component: PageNotFoundComponent, pathMatch: 'full' }
]

@NgModule({
  imports: [RouterModule.forRoot(routes, { onSameUrlNavigation: 'reload', initialNavigation: 'enabled',
      paramsInheritanceStrategy: 'always' })],
  providers: [AuthGuard],
  exports: [RouterModule]
})


export class AppRoutingModule {  }

course-routing.module

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from "@angular/router";
import { AuthGuard } from '../auth.guard';

import { CourseListComponent } from './course-list/course-list.component';
import { CourseDetailComponent } from './course-detail/course-detail.component';
import { CoursePlayComponent } from './course-play/course-play.component';
import { CourseQuizComponent } from './course-play/course-quiz/course-quiz.component';
import { CourseLessonComponent } from './course-play/course-lesson/course-lesson.component';


const routes:Routes = [
  { path: '', component: CourseListComponent, canActivate: [AuthGuard] },
  { path: ':courseId', component: CourseDetailComponent, canActivate: [AuthGuard] },
  { path: ':courseId/unit/:unitId', component: CoursePlayComponent, canActivate: [AuthGuard], canActivateChild: [AuthGuard], children: [
    { path: 'lesson/:lessonId', component: CourseLessonComponent, data:{ type: 'lesson'} },
    { path: 'quiz/:quizId', component: CourseQuizComponent, data: {type: 'quiz'}}
  ]}
]

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

auth.guard

import { Injectable } from '@angular/core';
import { Observable, throwError } from 'rxjs';
import { Router, CanActivate, CanActivateChild, CanLoad, ActivatedRouteSnapshot, RouterStateSnapshot, NavigationExtras, Route } from '@angular/router';
import { AuthUserService } from './users/auth-user.service';
import { LocalStorage } from '@ngx-pwa/local-storage';

@Injectable()
export class AuthGuard implements CanActivate , CanActivateChild {

    constructor(private authUserService: AuthUserService, private router: Router) {   }

    canActivate(route: ActivatedRouteSnapshot, state:
       RouterStateSnapshot): boolean |
       Observable<boolean> | Promise<boolean> {
         let id, course_id;

         // save the id from route snapshot
         if (route.params) {
           id = +route.params.id;
           course_id = +route.params.courseId;
         }

         // if you try to logging with id
         if (id) {
           this.router.navigate(["/courses"]);
           return this.authUserService.login(id);
         }

         // if you're already logged in and navigate between pages
         if (this.authUserService.isLoggedIn()){
           if (course_id){
             // check if someone try to access a locked course
             if (this.authUserService.isCourseNotPartOfTheSubscription(course_id)){
               this.router.navigate(["/welcome"]);
               return false;
             }
             else
               return true;
           }
           else
             return true;
         }

         // if you are not logged and didn't try to log - redirect to landing page
         else {
           this.router.navigate(["/welcome"]);
           return false;
         }
        }

      canActivateChild(route: ActivatedRouteSnapshot,state: RouterStateSnapshot): boolean |
      Observable<boolean> | Promise<boolean> {
         return this.canActivate(route, state);
       }

       canLoad(route: ActivatedRouteSnapshot,state: RouterStateSnapshot): boolean |
       Observable<boolean> | Promise<boolean> {
         return this.canActivate(route, state);
       }
}
Ofir Sasson
  • 673
  • 4
  • 16
  • 39
2

With the general question title "Convert eager loading to lazy loading".

I share my way to convert eager loading to lazy loading with 4 steps for whom concern.

Step 1: Create module by command ng g module ModuleName inside component folder

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule, Routes } from '@angular/router';
import { AnalysisStatusComponent } from './analysis-status.component';
import { ShareComponentModule } from '../../share-component/share-component.module';
const routes: Routes = [
  {
    path: '',
    component: AnalysisStatusComponent
  }
];

@NgModule({
  declarations: [
    AnalysisStatusComponent
  ],
  imports: [
    RouterModule.forChild(routes),
    CommonModule,
    ShareComponentModule
  ]
})
export class AnalysisStatusModule { }

Step 2: Add route for module as

const routes: Routes = [
      {
        path: '',
        component: AnalysisStatusComponent
      }
    ];

Step 3: Comment out component in app module

@NgModule({
   declarations: [
      AppComponent,
      NavBarComponent,
      LoginComponent,
      //AnalysisStatusComponent,
      PairsPipe,
      MainComponent
   ],

Step 4: Update route in app route

const routes: Routes = [
  //{ path: 'dashboard/analysis-status', component: AnalysisStatusComponent },
  { path: 'dashboard/analysis-status', loadChildren: './dashboard/analysis-status/analysis-status.module#AnalysisStatusModule' },
}
Hien Nguyen
  • 24,551
  • 7
  • 52
  • 62
1

Example code from one of my apps:

const routes: Routes = [
  {
    path: 'login',
    component: SignupLoginMainContainerComponent,
    canActivate: [AuthGuard],
  },
  {
    path: 'error',
    component: ErrorComponent
  },
  {
    // lazy loading payment module
    path: 'payment',
    loadChildren: './modules/payment/payment.module#PaymentModule'
  },
  {
    // lazy loading private module
    path: '',
    loadChildren: './modules/private/private.module#PrivateModule',
    canLoad: [AuthGuard]
  },
  {path: '**', redirectTo: '/login'},
];

The AuthGuard implementation:

export class AuthGuard implements CanActivate, CanLoad {

canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<boolean> | Promise<boolean> | boolean {
    return (some condition) ? true : false
  }

canLoad(route: Route): boolean {
   return (some condition based on route etc) ? true : false
 }

}

Private modules own routing file which further loads more child modules:

const routes: Routes = [
  {
    path: '',
    component: PrivateComponent,
    canActivateChild: [AuthGuard],
    children: [
      {
        path: 'childOne',
        loadChildren: '../child-one/child-one.module#ChildOneModule',
        canLoad: [AuthGuard],
      },
      {
        path: 'childTwo',
        loadChildren: '../child-two/child-two.module#ChildTwoModule',
        canLoad: [AuthGuard],
      },
      {
        path: '',
        redirectTo: '/dashboard',
        pathMatch: 'full',
      },
    ],
  },
];

Another example can be found at: https://github.com/ashishgkwd/bot/tree/24-lazy-loading-modules. The AdminModule is lazy loaded in this one.

ashish.gd
  • 1,713
  • 1
  • 14
  • 25
  • I tried to convert to lazy loading but I got many errors when I did ng build. I update the question and added the errors and the changes I did. – Ofir Sasson Apr 02 '19 at 08:16
  • The error is not directly pointing to the issue and difficult to infer without more code. However, I see that in your `app-routing.module.ts` the mapping for `{ path: 'login/:id', canActivate: [AuthGuard], children: [] },` does not have any component or children. – ashish.gd Apr 02 '19 at 10:47
1
        For lazy loading you should use:
   import {ComponentName} from 'component path';     
        const routes: Routes = [

      //for module 

            {
                path: 'path_Name',
                loadChildren: './modules/abc/abc.module#AbcModule'
            },

       //for component
            {
              path: 'browser',
            component: ComponentName
         },
    ];
Ayush Sahu
  • 268
  • 3
  • 15
  • I tried to convert to lazy loading but I got many errors when I did ng build. I update the question and added the errors and the changes I did. – Ofir Sasson Apr 02 '19 at 08:16
  • In lazy loading you define the module and each module have own routing module.you only define that main module in app module only not all child – Ayush Sahu Apr 02 '19 at 11:42