Angular 4.3 Interceptors for Lazy Loaded Modules

11,581

You don't have to create a provider for your interceptor. You should export your CoreModule with forRoot():

@NgModule({
  imports: [
    CommonModule,
    HttpClientModule,
    TranslateModule.forRoot({
      loader: {
        provide: TranslateLoader,
        useFactory: HttpLoaderFactory,
        deps: [HttpClient]
      }
    }),
    RouterModule.forRoot(
      [],
      {enableTracing: true}
    ),
  ],
  declarations: [],
  providers: [DatePipe]
})
export class CoreModule {
  constructor(@Optional() @SkipSelf() parentModule: CoreModule) {
    if (parentModule) {
      throw new Error(
        'CoreModule is already loaded. Import it in the AppModule only');
    }
  }

  static forRoot(): ModuleWithProviders {
    return {
      ngModule: CoreModule,
      providers: [
        {provide: 'Window', useValue: window},
        {provide: HTTP_INTERCEPTORS, useClass: RequestInterceptor, multi: true},
        SORT_TYPES_PROVIDER,
        ApiService,
        AnimationService,
        BillingService,
        UserService,
        ...
      ]
    };
  }
}

Then import it in your AppModule and forget about CoreModule imports at all. This is only one place it has to be explicitly used. All your lazy loaded modules will take your services etc by DI.

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    SharedModule,
    CoreModule.forRoot(),
    FeaturesModule,
    PublicModule,
    RouterModule.forRoot([])
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {
}

Also you don't need to create a separate routing module for each module. Just export what RouterModule.forChild returns and use it in imports of your module that needs it.

export const publicRouting: ModuleWithProviders = RouterModule.forChild([
  {
    path: 'login',
    pathMatch: 'full',
    component: SignInComponent,
    data: {
      breadcrumb: 'LGN_TL'
    },
    canActivate: [AuthenticatedGuard]
  },
  {
    path: '',
    component: GlobalComponent,
    loadChildren: '../protected/protected.module#ProtectedModule',
    canLoad: [AuthCanLoadGuard]
  },
  {path: '**', component: PageNotFoundComponent}
]);

UPD. suggestion for routing not a styleguide. Use RoutingModule as before (https://angular.io/guide/styleguide#angular-ngmodule-names)

Share:
11,581

Related videos on Youtube

Mukun
Author by

Mukun

Updated on June 04, 2022

Comments

  • Mukun
    Mukun almost 2 years

    What is the best practice to use core module service in lazy loaded feature module and feature child modules.
    As per Angular style guide I have the following

     app
       -core
           - core.module.ts
           -logger.service.ts
           -token-interceptor.service.ts
           -authentication.service.ts
       -shared
           -shared.module.ts
       -base module (my feature base , lazy loaded with router-outlet)
         -base.module.ts
         -base.routing.module.ts
         -base
           -base.component.ts
         -admin (lazy loaded , child module of base module)
          -admin.module.ts
          -admin.routing.ts
          -admin-component.ts 
         -report(lazy loaded , child module of base module, sibling of admin)
          -report.module.ts
          -report.routing.ts
          -report-component.ts
    

    If I add TokenInterceptorService as a provider in all feature module,then HTTP interceptor works. When I add it in App module,(but not in lazy loaded feature module) it does not intercept http request triggered in lazy loaded feature module.

    What is the best practice, of using services/interceptor declared in core module.

    app.module.ts

    @NgModule({
      declarations: [
        AppComponent,
        LoginComponent,
      ],
      imports: [
        BrowserModule,BrowserAnimationsModule, CoreModule, AppRoutingModule, FormsModule
      ], providers: [{ provide: HTTP_INTERCEPTORS, useClass: TokenInterceptorService, multi: true }],
      bootstrap: [AppComponent]
    })
    export class AppModule { }
    

    app-routing.module.ts

    @NgModule({
        imports: [
            RouterModule.forRoot([
                { path: 'login', component: LoginComponent },
                { path: '', redirectTo: 'base', pathMatch: 'full' },                   
                {  path: 'base', loadChildren: 'app/base/base.module#BaseModule'  }            
            ])
        ],
        exports: [
            RouterModule
        ]
    })
    export class AppRoutingModule {
    }
    

    core.module.ts

    @NgModule({
      imports: [
        CommonModule, HttpModule,  
      ],
      declarations: [],
      providers: [LoggerService, AuthenticationService]
    })
    export class CoreModule {
      constructor( @Optional() @SkipSelf() parentModule: CoreModule) {
        throwIfAlreadyLoaded(parentModule, 'CoreModule');
      }
    }
    

    token-interceptor.service.ts

    import { Injectable } from '@angular/core';
        import { Router } from '@angular/router';
        import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpErrorResponse } from '@angular/common/http';
        import { Observable } from 'rxjs/Observable';
        import 'rxjs/add/operator/do';
    
        import { AuthenticationService } from './authentication.service';
    
        @Injectable()
        export class TokenInterceptorService implements HttpInterceptor {
    
    
          constructor(public auth: AuthenticationService, private router: Router) { }
    
          intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
            console.log('Adding authorization header')
    
            request = request.clone({
              setHeaders: { authorization: this.auth.getToken() }
            });
            console.log('Added authorization header')
            return next.handle(request).do(event => { }, (err: HttpErrorResponse) => {
              console.log("Error ===>", err);
              if (err.error instanceof Error) {
                // A client-side or network error occurred. Handle it accordingly.
                console.log('An error occurred:', err.error.message);
              } else if (err.status == 401) {
    
                console.log('Status 401  unautorized');
                this.router.navigate(['/login']);
              } else {
                // The backend returned an unsuccessful response code.
                // The response body may contain clues as to what went wrong,
                console.log(`Backend returned code ${err.status}, body was: ${err.error}`);
              }
    
              return Observable.throw(new Error('Your custom error'));
            });;
          }
    
        }
    

    base.module.ts

     @NgModule({
          imports: [
            CommonModule, BaseRoutingModule
          ],
          declarations: [BaseComponent],
          providers: [],
        })
        export class BaseModule {
    
    
          constructor( @Optional() @SkipSelf() parentModule: BaseModule) {
    
            if (parentModule) {
              throw new Error(
                'BaseModule is already loaded. Import it in the AppModule only');
            }
          }
        }
    

    base.routing.module.ts

    @NgModule({
        imports: [
            RouterModule.forChild([
                {
                    path: '', component: BaseComponent,
                    children: [
    
                        { path: '', redirectTo: 'admin', pathMatch: 'full' },
                        { path: 'admin', loadChildren: 'app/base/admin/admin.module#AdminModule' },                         
                    ]
                }])
        ],
        exports: [
            RouterModule
        ]
    })
    export class BaseRoutingModule {
    }
    

    admin.module.ts

    @NgModule({
      imports: [
        CommonModule, FormsModule, HttpClientModule, AdminRoutingModule,BusyModule
      ],
      declarations: [UserListComponent, UserComponent, MenuListComponent, MenuComponent, CodeListComponent, CodeComponent],
      providers: [CodeService, UserService, MenuService,{ provide: HTTP_INTERCEPTORS, useClass: TokenInterceptorService, multi: true }]
    })
    export class AdminModule { }
    

    admin.routing.module.ts

    @NgModule({
        imports: [
            RouterModule.forChild([
    
                {
                    path: '',
                    children: [
                        { path: '', redirectTo:'code-list', pathMatch: 'full'  },
                        { path: 'code-list', component: CodeListComponent },
                        { path: 'code/:id', component: CodeComponent },
    
                        { path: 'user-list', component: UserListComponent },
                        { path: 'user/:id', component: UserComponent },
    
                        { path: 'menu-list', component: MenuListComponent },
                        { path: 'menu/:id', component: MenuComponent },                    
                    ]
                }
            ])
        ],
        exports: [
            RouterModule
        ]
    })
    export class AdminRoutingModule {
    }
    
  • Mukun
    Mukun over 6 years
    it is not working, I exported CoreModule, but request are not intercepted.
  • Mukun
    Mukun over 6 years
    In angular angular.io/guide/router , each router file is defined as module. (AppRoutingModule,HeroRoutingModule ), you suggest to export RouterModule.forChild , while both works for me but I don't know the pros and cons between them
  • Danil Gudz
    Danil Gudz over 6 years
    oh, it's okay for routing. Found in styleguides angular.io/guide/styleguide#angular-ngmodule-names . CoreModule has to include all the providers. There should be defined an interceptor. And only one place you have to import CoreModule is AppModule and you don't need to create another providers for existing interceptor in CoreModule. Also you have to remove HttpClientModule from every submodules. It causes not to intercept your requests as your submodule use a new instance of HttpClientModule.
  • Danil Gudz
    Danil Gudz over 6 years
    If you want to use services from this module it will be injected from dependencies tree from AppModule that has import for CoreModule that has HttpClientModule import. Look in my example. Import HttpClientModule only in CoreModule
  • alcfeoh
    alcfeoh over 5 years
    Thanks, your solution saved my day