Angular 4.3 Interceptors for Lazy Loaded Modules
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)
Related videos on Youtube
Mukun
Updated on June 04, 2022Comments
-
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 followingapp -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 over 6 yearsit is not working, I exported CoreModule, but request are not intercepted.
-
Mukun over 6 yearsIn 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 over 6 yearsoh, 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 importCoreModule
isAppModule
and you don't need to create another providers for existing interceptor inCoreModule
. Also you have to removeHttpClientModule
from every submodules. It causes not to intercept your requests as your submodule use a new instance ofHttpClientModule
. -
Danil Gudz over 6 yearsIf you want to use services from this module it will be injected from dependencies tree from
AppModule
that has import forCoreModule
that hasHttpClientModule
import. Look in my example. ImportHttpClientModule
only inCoreModule
-
alcfeoh over 5 yearsThanks, your solution saved my day