0

I'm trying to set an opaquetoken in the providers using an observable. Reason being is I'm reading the value via the Http provider (external JSON file).

This is what I'm trying to do

    {
        provide: SOME_OPAQUE_TOKEN,
        useFactory: (configService: ConfigService) => {
            configService.getPath('campaigns')
               .subscribe((res) => {
                   ???
               });
        },
        deps: [ConfigService],
    },

So obviously this wont work but I'm wondering if there is a solution for this sort of problem?

Or if its actually possible to construct a service using useFactory where one of your parameters is retrieved asynchronously.

Is it possible at all?

Edit: Solution using APP_INITIALIZER

In AppModule:

{
  provide: APP_INITIALIZER,
  useFactory: (configService: ConfigService) => () => configService.load(),
  multi: true,
  deps: [ConfigService, Http],
},

In ConfigService load():

public load(): Promise<Configuration> {
    let promise =
        this.http
            .get('config.json')
            .map(m => m.json())
            .toPromise();

    promise
        .then(config => this.appConfig = config);

    return promise;
}

Once we set the appConfig we can use it to set the OpaqueToken:

    {
        provide: BASE_PATH,
        useFactory: (configService: ConfigService) => configService.appConfig.basePath, deps: [ConfigService],
    },
Yodacheese
  • 4,787
  • 5
  • 34
  • 42
  • You need to resolve the dependency before the app will be initialized. I guess that [APP_INITIALIZER](https://stackoverflow.com/search?q=app_initializer) helps. – Estus Flask Sep 30 '16 at 00:42
  • Thanks @estus, is there an example where this is used? – Yodacheese Sep 30 '16 at 01:42
  • It isn't covered by the reference yet. You may use search, there's a plenty of answered question on SO that cover APP_INITIALIZER, e.g. http://stackoverflow.com/a/38128065/3731501 . – Estus Flask Sep 30 '16 at 04:03
  • Looks interesting its worrying that it is undocumented and marked as "experimental". Regardless I tried to use it and got: Unhandled Promise rejection: appInits[i] is not a function ; Zone: ; Task: Promise.then ; Value: TypeError: appInits[i] is not a function. – Yodacheese Oct 02 '16 at 21:17
  • 1
    APP_INITIALIZER should be a function that returns a promise. I.e. `useFactory: (configService: ConfigService) => () => configService.load()`. [Here's how it works](https://github.com/angular/angular/blob/master/modules/%40angular/core/src/application_init.ts#L34) (it isn't documented but quite straightforward). – Estus Flask Oct 02 '16 at 21:34
  • Great that worked! Thanks @estus feel free to add it as an answer. – Yodacheese Oct 02 '16 at 21:54
  • Sure, you're welcome. – Estus Flask Oct 02 '16 at 22:24
  • @estus your link is now broken. Can you please fix or let me know the url? – LanceM Apr 14 '17 at 21:05
  • @LanceM It's here now https://github.com/angular/angular/blob/4.0.0/packages/core/src/application_init.ts – Estus Flask Apr 14 '17 at 21:16

1 Answers1

2

APP_INITIALIZER undocumented multi-provider is supposed to be used to resolve app dependencies asynchronously.

An initializer is supposed to be a function that returns a promise (for async initialization) or any other value (for sync initialization). Since APP_INITIALIZER is multi-provider, there can be many initializers, they will be executed in parallel and awaited. The implementation is here.

It can be defined as a provider in module:

{
  provide: APP_INITIALIZER,
  useFactory: (...deps...) => () => promise,
  deps: [...deps...],
  multi: true
}

Or for an initializer without dependencies:

{
  provide: APP_INITIALIZER,
  useValue: () => promise,
  multi: true
}
Estus Flask
  • 206,104
  • 70
  • 425
  • 565