Jay Martha
Jay Martha

Reputation: 111

Angular injecting different service implementation to a child module

I have a class implementing ErrorHandler and is provided at root level. I have 3 modules in my app of which 2 of them can use ErrorHandler at root level but one of them should have different version of ErrorHandler.

I have tried creating two implementations of ErrorHandler, one of which is provided at root level, I have decorated second service with {providedIn: ThreePageModule}, but this doesn't seems like working as whenever there is an error ex: HttpErrorResponse the root level ErrorHandler kicks in. I have looked at the angular documentation and it says when a child Module is lazy loaded it gets the provided service. Am I missing something here??

Below is the app.module.ts

@NgModule({
declarations: [AppComponent],
entryComponents: [],
imports: [
FormsModule,
BrowserModule,
HttpClientModule,
AppRoutingModule
],
providers: [
 ....other services...
{
  provide: HTTP_INTERCEPTORS,
  useClass: HttpConfigInterceptor,
  multi: true
},
{ provide: ErrorHandler, useClass: ExceptionHandlerService },
],
bootstrap: [AppComponent]
})
export class AppModule {}

AppRoutingModule.ts:

@NgModule({
imports: [
 RouterModule.forRoot(appRoutes)
],
exports: [
 RouterModule
]
})
export class AppRoutingModule { }

appRoutes.ts:

 export const appRoutes: Routes = [
   {
    path: 'one',
    loadChildren: './../one/one.module#OnePageModule'
 },
  {
      path: 'two',
      loadChildren: './../two/two.module#TwosPageModule'
  },
  {
    path: 'three',
    loadChildren: './../three/three.module#ThreePageModule'
  }
 ];

ThreeModule.ts

@NgModule({
imports: [
 ...Imports...
],
entryComponents: [
 ThreeComponent
],
providers: [DatePipe],
declarations: [
....Declarations.....
]
})
export class ClaimsPageModule { }

ThreeErrorHandlerService.ts

@Injectable({
  providedIn: ThreePageModule
})
export class ThreeErrorHandlerService implements ErrorHandler {

constructor() { }

handleError(error: Error | HttpErrorResponse): void {
  if (error instanceof HttpErrorResponse) {
   return;
  }
 }
}

What I want is to use ThreeErrorHandlerService in ThreePageModule while still using ExceptionHandlerService in rest of the two modules.

Is this the way we achieve it or am I doing it wrongly.

Upvotes: 3

Views: 2683

Answers (1)

Andrei Gătej
Andrei Gătej

Reputation: 11924

I think there is a way to achieve this.

app.tokens.ts


export function errorHandlerFactory (r: Router): AbstractErrorHandler {
  return r.url === '/foo' ? new ErrorHandlerThree('foo error!') : new ErrorHandlerOne('not foo !!')
}

export abstract class AbstractErrorHandler {
  abstract getCrtErr: () => string;
}

export class ErrorHandlerOne implements AbstractErrorHandler {
  constructor (public crtErr: string) { }

  getCrtErr () { return 'err!' }
}

export class ErrorHandlerThree implements AbstractErrorHandler {
  constructor (public crtErr: string) { }

  getCrtErr () { return 'err!' }
}

app.module.ts

@Component({
  selector: 'my-app',
  template: `
    <button [routerLink]="'foo'">foo route</button>

    <router-outlet></router-outlet>
  `,
})
class AppComponent {
  constructor (err: AbstractErrorHandler) {
    console.log(err) // not foo !!
  }
}

const routes: Routes = [
  { path: 'foo', loadChildren: () => import('./foo/foo.module').then(m => m.FooModule,) },
]

@NgModule({
  imports: [
    BrowserModule,
    RouterModule.forRoot(routes),
  ],
  declarations: [
    AppComponent
  ],
  providers: [
    { provide: AbstractErrorHandler, useFactory: errorHandlerFactory, deps: [Router] }
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

foo.module.ts

@Component({
  selector: 'foo-comp',
  template: `this is foo!`,
})
export class FooComp {
  constructor (err: AbstractErrorHandler) {
    console.log('foo', err) // foo error!
  }
}

const routes: Routes = [
  { path: '', component: FooComp, }
]

@NgModule({
  declarations: [FooComp],
  imports: [
    CommonModule,
    RouterModule.forChild(routes),
  ],
  providers: [
    { provide: AbstractErrorHandler, useFactory: errorHandlerFactory, deps: [Router] }
  ],

Note that you might need to track class instances inside app.tokens.ts as you might not want a new instance every time a component injects your this dependency.

FactoryProvider.

ng-run demo.

Upvotes: 3

Related Questions