遅延ロードされた機能モジュールと機能の子モジュールでコアモジュールサービスを使用するためのベストプラクティスは何ですか。
Angularスタイルガイドに従って、私は以下を持っています
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
すべての機能モジュールでプロバイダーとしてTokenInterceptorServiceを追加すると、HTTPインターセプターが機能します。 Appモジュールに追加すると(遅延ロード機能モジュールではなく)、遅延ロード機能モジュールでトリガーされたhttpリクエストがインターセプトされません。
コアモジュールで宣言されたサービス/インターセプターを使用するベストプラクティスは何ですか。
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 {
}
インターセプターのプロバイダーを作成する必要はありません。 CoreModule
を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,
...
]
};
}
}
次に、それをAppModule
にインポートし、CoreModule
インポートについてはまったく忘れます。これは、明示的に使用する必要がある1つの場所にすぎません。すべての遅延ロードされたモジュールは、DIによってサービスなどを受け取ります。
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
SharedModule,
CoreModule.forRoot(),
FeaturesModule,
PublicModule,
RouterModule.forRoot([])
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {
}
また、モジュールごとに個別のルーティングモジュールを作成する必要はありません。 RouterModule.forChildが返すものをエクスポートし、それを必要とするモジュールのimports
で使用するだけです。
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。スタイルガイドではなくルーティングの提案。以前と同じようにRoutingModule
を使用します( https://angular.io/guide/styleguide#angular-ngmodule-names )