jvandemo / generator-angular2-library

Yeoman generator to create an Angular library
MIT License
752 stars 122 forks source link

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

Open brentchow opened 6 years ago

brentchow commented 6 years ago

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 generator-angular2-library@10.1.0

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
  }
vonsko commented 6 years ago

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

gkaran commented 6 years ago

Anyone found a solution on this?

vonsko commented 6 years ago

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)

gkaran commented 6 years ago

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

jotwea commented 6 years ago

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()]

brentchow commented 6 years ago

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.

vijayakumar-psg587 commented 6 years ago

@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 commented 6 years ago

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 commented 6 years ago

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`

deepthan commented 6 years ago

@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?

gkaran commented 6 years ago

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

kshitij-tf-zz commented 6 years ago

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

varosengineer commented 5 years ago

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

zhangjianrencai commented 5 years ago

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

rpasechnikov commented 4 years ago

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

rp0m commented 4 years ago
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 commented 4 years ago

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

KLiFF2606 commented 4 years ago

Any one noticed the use of LIB_CONFIG and WEB_CONFIG?