generator-angular2-library icon indicating copy to clipboard operation
generator-angular2-library copied to clipboard

Adding Library configs using `forRoot()` and `InjectionToken`

Open brentchow opened this issue 7 years ago • 18 comments

I'm having trouble adding library configs using forRoot() and InjectionToken.

When passing in environment variables via forRoot(config), I can access the variables in the forRoot method, but they are not injecting into services.

I've added some comments in my sample code to show where things are breaking.

I originally generated my project using [email protected]

app.module.ts (App that uses Library)

@NgModule({
  declarations: [AppComponent],
  imports: [
    CoreModule.forRoot(environment) // {key: 'foobar'}
  ],
  providers: [...],
  bootstrap: [AppComponent],
})
export class AppModule { }

index.ts (Library)

import { NgModule, ModuleWithProviders } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Config, LIB_CONFIG } from './config';
import { FooService } from './foo.service';

@NgModule({
  imports: [CommonModule],
  providers: [FooService],
})

export class CoreModule {
  static forRoot(config: Config): ModuleWithProviders {
    console.log(config); // prints:  `{key: 'foobar'}`
    return {
      ngModule: CoreModule,
      providers: [
        {provide: LIB_CONFIG, useValue: config} // If I hard code `useValue: {key: 'FooBar'}`, instead of using `config` it works... weird.
      ],
    };
  }
}

config.ts (Library)

import { InjectionToken } from '@angular/core';

export interface Config {
  key?: string;
}

export const LIB_CONFIG = new InjectionToken<Config>('LIB_CONFIG');

foo.service.ts (Library)

import { Inject, Injectable } from '@angular/core';

import { Config, LIB_CONFIG } from './config';

@Injectable()
export class FooService {
  private key: string;

  constructor(
    @Inject(WEB_CONFIG) private config: Config,
  ) {
    console.log(config); // prints: `undefined`
    this.key = config.key; // can't read property 'key' of undefined
  }

brentchow avatar Oct 14 '17 05:10 brentchow

Hey, I've got exactly same issue but with one difference: this occurs only with -prod enabled

vonsko avatar Nov 17 '17 12:11 vonsko

Anyone found a solution on this?

gkaran avatar Jan 17 '18 06:01 gkaran

Well, I didn't - I think wrote some workaround - but will be back at the problem in next days/week. … and last thing I've established was that problem is more in my consuming project rather than builded module (try to create clean project via ng new and install module, than build prod - it might work… if so problem is in building consuming project)

vonsko avatar Jan 17 '18 06:01 vonsko

The problem is in the consuming project, yes. Will keep on digging and if I find something will report my findings

gkaran avatar Jan 17 '18 07:01 gkaran

You mentioned that you get this bug only with -prod enabled, so I guess it has to do with AoT compiler and the statement cannot be parsed ahead. But there is a trick that worked for me by using the spread operator. You can try this in your provider definition:

providers: [
        ...[{provide: LIB_CONFIG, useValue: config}]
]

or if you wanna provide a default config as a fallback you can do this way:

...(config) ? [{provide: LIB_CONFIG, useValue: config}] : [{provide: LIB_CONFIG, useValue: new DefaultConfig()]

jotwea avatar Mar 29 '18 11:03 jotwea

Thanks, @jotwea. That's interesting.

We abandoned this library and moved on to doing things a different way. I can't easily test to validate that it resolves the issue, but maybe @vonsko or @gkaran can speak to it.

brentchow avatar Mar 29 '18 15:03 brentchow

@brentchow : I am little confused. Is there a reason behind adding "WEB_CONFIG" while injecting to the service - FooService. I believe you will have only LIB_CONFIG instance which is an object - {key:'foobar'} when the angular container is initialised.

I am unsure if this will work though, but can you remove providers altogether from the @NgModule in CoreModule and add it to static forRoot() injector like this

static forRoot(config: Config): ModuleWithProviders { console.log(config); return { ngModule: CoreModule, providers: [FooService, {provide: LIB_CONFIG, useValue: config} ], }; }

vijayakumar-psg587 avatar Sep 04 '18 08:09 vijayakumar-psg587

Also I believe using console.log before the return statement in forRoot() static method causes issues with both AOT and JIT compilation . So please avoid it incase you encounter the exception Error during template compile of 'AppModule' Function calls are not supported in decorators

vijayakumar-psg587 avatar Sep 04 '18 09:09 vijayakumar-psg587

This actually worked for me in a sample project. I use the following versions `Angular CLI: 6.0.8 Node: 8.11.1 OS: darwin x64 Angular: 6.1.1 ... common, compiler, compiler-cli, core, forms, http ... language-service, platform-browser, platform-browser-dynamic ... router

Package Version

@angular-devkit/architect 0.6.8 @angular-devkit/build-angular 0.6.8 @angular-devkit/build-optimizer 0.6.8 @angular-devkit/core 0.6.8 @angular-devkit/schematics 0.6.8 @angular/animations 6.1.3 @angular/cdk 6.4.5 @angular/cli 6.0.8 @angular/material 6.4.5 @ngtools/webpack 6.0.8 @schematics/angular 0.6.8 @schematics/update 0.6.8 rxjs 6.2.2 typescript 2.7.2 webpack 4.8.3`

vijayakumar-psg587 avatar Sep 05 '18 05:09 vijayakumar-psg587

@brentchow hi, I have the error function calls are not supported in decorators but ... was called when i use --prod, can you give me a solution ? thanks a lot.

@gkaran hi, Have you found a solution?

deepthan avatar Sep 07 '18 10:09 deepthan

Sorry @deepthan nothing new to report as I too abandoned this and converted into an angular/cli library project

gkaran avatar Sep 07 '18 10:09 gkaran

@vijayakumar-psg587 can you please share git repo location for sample project?

kshitij-tf-zz avatar Sep 20 '18 13:09 kshitij-tf-zz

Hey @brentchow, could you share that "different way" of doing this? I'm facing similar problems. Thanks.

varosengineer avatar Nov 29 '18 16:11 varosengineer

@brentchow hi, I have met the same error when I use --prod to build my consume project. could you please give me some advise?

zhangjianrencai avatar Apr 08 '19 01:04 zhangjianrencai

Same issue here. Can't use 'of()' operator in the forRoot() when using prod build

rpasechnikov avatar Feb 21 '20 02:02 rpasechnikov

ComponentLibraryModule.forRoot({
      appLinks: of(APP_LINKS)
    }),

Works and build OK in non prod. Prod build results in: ERROR in src\app\app.module.ts(100,22): Error during template compile of 'AppModule' Function calls are not supported in decorators but 'of' was called.

rp0m avatar Feb 21 '20 02:02 rp0m

I realized a simple fix workaround for it is to wrap the observable within a function, ie:

export function appLinksFactory(): Observable<AppLinks[]> {
    of(APP_LINKS)
}

ComponentLibraryModule.forRoot({
        appLinks: appLinksFactory
    }),

You will obviously have to change your configuration object to match, but this works ok and builds in all environments

rp0m avatar Feb 23 '20 02:02 rp0m

Any one noticed the use of LIB_CONFIG and WEB_CONFIG?

KLiFF2606 avatar Apr 04 '20 10:04 KLiFF2606