Предоставьте инъекционный токен после ИНИЦИАЛИЗАЦИИ приложения с разрешенной конфигурацией

#angular #angular-providers #injection-tokens

Вопрос:

Мне нужно получить конфигурацию с помощью фабрики, которая будет разрешена во время ИНИЦИАЛИЗАЦИИ приложения (с помощью поставщика APP_INITIALIZER).

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

Это обеспечивается с помощью AppModule:

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

Затем мне нужно использовать эти данные конфигурации для ввода чего-либо внутри другого InjectionToken, но если я предоставлю конкретный injectiontoken, используя конфигурацию, предоставленную во время инициализации приложения, этот процесс выполняется до выполнения APP_INITIALIZER.

 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() {}
}
 

Как я могу предоставить токен для инъекции после инициализации приложения?

Ответ №1:

То, что у вас здесь есть, должно действительно работать, если ваша loadConfig фабрика возвращает функцию вместо фактического обещания:

 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);
    });
};
 

Предоставление APP_INITIALIZER выглядит точно так же, как в вашем коде:

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

Когда вы настраиваете следующий токен внедрения, у вас должна быть доступная конфигурация для использования

 {
  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],
},
 

Единственные асинхронные фабрики, которые разрешены в Angular, — это те, которые вы используете с APP_INITIALIZER токеном внедрения, но имейте в виду, что из них вам нужно возвращать функцию вместо фактического значения.

Комментарии:

1. Я отредактировал файл loadConfig, возвращающий функцию, которая возвращает обещание<Config>. Пробовал, как вы упомянули, но не работает.

2. Я попробовал решение, которое я опубликовал, и когда я FORMATS вводлю маркер инъекции в компонент, я получаю ожидаемое значение "formats" .

3. Решено, конфигурация не была задана в конкретном вызове, который я вызывал, у меня было три загрузки конфигурации (одна из статического файла, другие из сред), затем мне нужно было загрузить из файла не переменные env, так как они были загружены в APP_INITIALIZER, а другие из более позднего вызова. Ваш код работает нормально.

4. Хороший улов, я рад, что вы разобрались с этим :).