3

I need to get the config using a factory that will be resolved during the APP INITIALIZATION (using the APP_INITIALIZER provider).

export function loadConfig(): () => Promise<Config> {
    // return promised config
}

That is provided using the AppModule:

providers: [{
  provide: APP_INITIALIZER,
  useFactory: loadConfig,
  deps: [HttpClient, ConfigService],
  multi: true
}]

Then I need to use that configuration data in inject something inside another InjectionToken, but if I provide the specific injectiontoken using the config provided during the app initialization, that process is executed before the APP_INITIALIZER execution.

export const FORMATS = new InjectionToken<Formats>("formats")

export assignFormat(configService: ConfigService) {
    return configService.getFormats(); // Needs to execute after APP_INITIALIZER, not before
}
providers: [{
  provide: APP_INITIALIZER,
  useFactory: loadConfig,
  deps: [HttpClient, ConfigService],
  multi: true
}, {
  provide: FORMATS,
  useFactory: assignFormat,
  deps: [ConfigService]
}]
@Injectable({ providedIn: "root" })
export class ConfigService {
    constructor() {}
    getFormats() {}
}

How can I do to provide the injection token after the APP initialization?

1 Answer 1

4

What you have here should actually work, if your loadConfig factory returns a function instead of the actual promise:

const loadConfig = (configService: ConfigService) => {
  return () =>
    new Promise<void>((resolve, reject) => {
      // I added the timeout to simulate a delay
      setTimeout(() => {
        // you might be using a http call to get the config from the server instead.
        // Make sure you keep the config that you fetched in the service;
        // this way you can inject the service in the next factory function 
        // and have the config available
        configService.config = {
          formats: 'formats',
        };
        resolve();
      }, 1000);
    });
};

The provision of the APP_INITIALIZER looks exactly like in your code:

{
  provide: APP_INITIALIZER,
  useFactory: loadConfig,
  deps: [ConfigService],
  multi: true,
},

When you set up the next injection token, you should have the config available to use

{
  provide: FORMATS,
  useFactory: (configService: ConfigService) => {
    // getFormats method must not be async, it needs to return the actual 
    // formats that were fetched during the app initialization phase
    return configService.getFormats();
  },
  deps: [ConfigService],
},

The only async factories that are allowed in Angular are the ones you use with the APP_INITIALIZER injection token, but beware that from these, you need to return a function instead of the actual value.

Sign up to request clarification or add additional context in comments.

4 Comments

I edited the loadConfig is returning a function that returns the Promise<Config>. Tried as you mentioned but doesn't work.
I tried the solution I posted and when I inject the FORMATS injection token in a component, I get the expected value "formats".
Solved, the config was no set in the specific call that I have been calling, I had three config load (one from a static file, others from environments) then I needed to load from the file not the env variables since that was loaded in the APP_INITIALIZER and others from later call. Your code works fine.
Nice catch, I am glad you sorted it out :).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.