web-dev-qa-db-ja.com

Angular 4.3遅延ロードされたモジュールのインターセプター

遅延ロードされた機能モジュールと機能の子モジュールでコアモジュールサービスを使用するためのベストプラクティスは何ですか。
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 {
}
10
Mukun

インターセプターのプロバイダーを作成する必要はありません。 CoreModuleforRoot()でエクスポートする必要があります:

@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

6
Danil Gudz