3

Assuming that we have a module which does some authentication stuff in it.

I want to set some variables while adding the module to the project.

I've seen that the routing module does something similar with the function forRoot(routes).

So I tried the same but it kind a feels not correct.

My authentication module (should be configurable)

@NgModule({
  imports: [
    CommonModule
  ],
  declarations: [],
  providers: []
})
export class AuthenticationModule {
  static forRoot(authMode: AuthMode) {
    return {
      ngModule: AuthenticationModule,
      providers: [
        {
          useValue: authMode,
          provide: AuthMode
        },
        {
          provide: AuthenticationGuard,
          useFactory: authFactory,
          deps: [AuthMode, Router]
        }
      ]
    }
  }
}

export function authFactory(authMode: AuthMode, router: Router) {
  return new AuthenticationGuard(authMode, router);
}

AuthMode is just a enum holding a value, to compare the selected mode later on in the AuthenticationGuard.

My app.module.ts adds the module like this:

@NgModule({
  declarations: [ // some stuff ]
  imports: [
    BrowserModule,
    // ... some other stuff
    AuthenticationModule.forRoot(AuthMode.OAuthLocalStorage)
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {
}

The AuthenticationGuard injects the authMode by value like this:

@Injectable()
export class AuthenticationGuard implements CanActivate {
  public storage: Storage;

  constructor(@Inject(AuthMode) authMode, private router: Router) {
    console.log('selected authMode is ', AuthMode[authMode]);
}

This works fine when using the guard with the routes and canActivate property. But once I inject it to some other service (inside another module), I get this error:

Error: Invalid provider for the NgModule 'AuthenticationModule' - only instances of Provider and Type are allowed, got: [?undefined?, ...] at SyntaxError.ZoneAwareError

So my basic question would be:

  • Is this the way how to make a ngModule configurable
  • And what would solve the problem above?

Build with Angular 2 and angular-cli (beta 25)

eav
  • 1,925
  • 7
  • 24
  • 34

1 Answers1

0

What about storing the setting in a special variable and using a factory to inject it?

let authModeSetting: AuthMode;

@NgModule({
  imports: [
    CommonModule
  ],
  providers: [
    {
      useFactory: () => authModeSetting,
      provide: AuthMode
    },
    AuthenticationGuard
  ]
})
export class AuthenticationModule {

  static forRoot(authMode: AuthMode) {
    authModeSetting = authMode;

    return AuthenticationModule;
  }

}
smnbbrv
  • 19,108
  • 9
  • 62
  • 100
  • Nice idea. Tried it out, but didn't work because of this error: `Error encountered resolving symbol values statically. Calling function 'AuthenticationModule', function calls are not supported. Consider replacing the function or lambda with a reference to an exported function, resolving symbol AppModule in app.module.ts, resolving symbol AppModule in app.module.ts` – eav Feb 02 '17 at 08:57
  • `Calling function 'AuthenticationModule'` sounds weird, where is it called as a function? – smnbbrv Feb 02 '17 at 09:10
  • Only in the `imports` of the `AppModule`, as seen in the code of the question: `AuthenticationModule.forRoot(AuthMode.OAuthLocalStorage)` Maybe because of AOT (default in `ng serve` of the `angular-cli`)? https://github.com/angular/angular-cli/issues/3674 – eav Feb 02 '17 at 09:16