web-dev-qa-db-fra.com

Angular 4.3 Intercepteurs pour modules chargés paresseux

Quelle est la meilleure pratique pour utiliser le service de module principal dans le module de fonctionnalité chargé paresseusement et les modules enfants de fonctionnalité.
Selon Angular j'ai ce qui suit

 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

Si j'ajoute TokenInterceptorService en tant que fournisseur dans tous les modules de fonctionnalités, alors l'intercepteur HTTP fonctionne. Lorsque je l'ajoute dans le module App (mais pas dans le module de fonctionnalité chargé paresseusement), il n'intercepte pas la demande http déclenchée dans le module de fonctionnalité chargé paresseux.

Quelle est la meilleure pratique, d'utiliser les services/intercepteurs déclarés dans le module de base.

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

Vous n'êtes pas obligé de créer un fournisseur pour votre intercepteur. Vous devez exporter votre CoreModule avec 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,
        ...
      ]
    };
  }
}

Importez-le ensuite dans votre AppModule et oubliez les importations de CoreModule. Ce n'est qu'un endroit où il doit être explicitement utilisé. Tous vos modules chargés paresseux prendront vos services, etc. par DI.

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

De plus, vous n'avez pas besoin de créer un module de routage distinct pour chaque module. Exportez simplement ce que RouterModule.forChild retourne et utilisez-le dans imports de votre module qui en a besoin.

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 pour le routage pas un guide de style. Utilisez RoutingModule comme auparavant ( https://angular.io/guide/styleguide#angular-ngmodule-names )

6
Danil Gudz