0

I'm trying to get my code to compile during --aot flag, but it fails where I try to load environment configuration into my logging service that I import in my app.module.ts file. I have code looking like this that runs fine with "ng serve"

This is the structure of what I want to pass over dynamically to my singleton logging service.

// In it's own ts file, not in the module
export interface AppConfiguration {   
  user: User;
  application: ApplicationConfig;
}

// In it's own ts file, not in the module
export const appConfig = (): AppConfiguration => {
  // ... logic happens here to set up the environment, load cookies etc that will result something like this
  const userConf: User = {username: 'ronnrak', name: 'Ronny Raketgevär', id: 1, location: 'a place'};
  const appConf: ApplicationConfig = {defaultLogLevel: 'debug', defaultLogType: 'console'};
  return {user: userConf, application: appConf};
}

logging.module.ts:

export const APP_CONFIG = new InjectionToken<AppConfiguration>('app_config');

@NgModule({
  imports: [CommonModule]
})
export class LoggingModule {
  constructor(@Optional() @SkipSelf() parentModule: LoggingModule) {
    if(parentModule) {
      throw new Error('LoggingModule is already loaded, import in AppModule only.');
    }
  }

  static forRoot(config: AppConfiguration): ModuleWithProviders {
    return {
      ngModule: LoggingModule,
      providers: [
        {provide: APP_CONFIG, useValue: config},
        {provide: LoggingService, useFactory: (conf: AppConfiguration) => {
          return conf.Application.defaultLogType === 'console' ? new ConsoleLoggingService(conf) : new RestLoggingService(conf);
        }, deps:[APP_CONFIG]}
      ]
    };
  }
}

app.module.ts where the error occures

const applicationConfigData: AppConfiguration = appConfig(); <-- error points here

@NgModule({
  declarations: [
    ...
  ],
  imports: [
    ...
    LoggingModule.forRoot(applicationConfigData),
    ...
  ],
  providers: [
    {provide: APP_CONFIG, useValue: applicationConfigData},
    {provide: StoreService, useFactory: (conf: AppConfiguration) => new StoreService(conf), deps: [APP_CONFIG]},
    ...
  ],
  schemas: [
    ...
  ],
  bootstrap: [
    ...
  ]
})
export class AppModule {}

This is the error I get while running --aot

ERROR in apps\project\src\app\app.module.ts(17,49): Error during template compile of 'AppModule'
  Function expressions are not supported in decorators in 'appConfig'
    'appConfig' references 'appConfig'
      'appConfig' contains the error at libs\environment\src\lib\init\app-init.ts(19,26)
        Consider changing the function expression into an exported function.

I've tried to change appConfig without the lambda expression and just a clear exported function, but it didn't work either.

ERROR in apps\project\src\app\app.module.ts(17,49): Error during template compile of 'AppModule'
  Function calls are not supported in decorators but 'appConfig' was called.

How do I load dynamic data into my singleton service from app.module?

Sam
  • 418
  • 2
  • 6
  • 18
  • 2
    This is a well known issue, several are opened on Angular's repository. You simply can't use function calls into decorators. –  Jul 25 '18 at 08:50
  • 1
    @trichetriche Good point. Good example of the problem/fix here https://github.com/matheushf/ng2-date-countdown/issues/6#issuecomment-364641790 – Drenai Jul 25 '18 at 08:58

1 Answers1

3

You should export the useFactory function of the LoggingService and the StoreService, because, and I quote trichetriche

You simply can't use function calls in decorators

LoggingService

export function loggingFactory(conf: AppConfiguration) {
  return conf.Application.defaultLogType === 'console' ? 
    new ConsoleLoggingService(conf) : new RestLoggingService(conf);
}

{
  provide: LoggingService, 
  useFactory: loggingFactory, 
  deps:[APP_CONFIG]
}

And the StoreService:

export function storeFactory(conf: AppConfiguration) {
  return new StoreService(conf);
}

{
  provide: StoreService, 
  useFactory: storeFactory, 
  deps: [APP_CONFIG]
},

But this one seems a bit obsolete, because you can just use the service directly and inject the APP_CONFIG using @Inject()

@Injectable()
export class StoreService {
  constructor(@Inject(APP_CONFIG) appConfig: AppConfiguration) {}
}
Poul Kruijt
  • 69,713
  • 12
  • 145
  • 149
  • Where should I export the useFactory functions to, and how do I call them? – Sam Jul 25 '18 at 09:04
  • @Sam You can just place them above the `@NgModule` declaration, and you don't have to call them, you use them as `useFactory` values, and angular will call them for you – Poul Kruijt Jul 25 '18 at 09:28
  • 1
    It worked! I had to remove my LoggingModule import and add it to providers, using your code example. Thank you! – Sam Jul 25 '18 at 11:00