Angular — есть ли способ отложить создание службы до тех пор, пока приложение не будет инициализировано асинхронно?

У меня есть приложение Angular 5 с перехватчиком HTTP, который вызывает другую службу всякий раз, когда перехватывает запрос.
Я хочу настроить службу с помощью файла конфигурации, поэтому я попытался использовать службу, которая загружает конфигурацию асинхронно во время инициализации приложения ( используя хук APP_INITIALIZER).
Проблема в том, что служба, которую я хочу использовать, создается до того, как служба конфигурации завершит загрузку конфигурации из файла конфигурации.

Есть ли способ отложить создание экземпляра службы до тех пор, пока служба конфигурации не завершит загрузку конфигурации?


Код

Вы можете увидеть полный пример в stackblitz.
Наиболее важные части приведены ниже:

перехватчик.service.ts

export class InterceptorService implements HttpInterceptor {
  constructor(private dependency: InjectedDependencyService) { }

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    // use InjectedDependencyService to do something on every intercepted request
    const userId = this.dependency.getUserId();
    console.log("user id:", userId);
    return next.handle(request);
  }
}

введенная зависимость.service.ts

export class InjectedDependencyService {
  userId;

  constructor(private initializer: InitializerService) {
    this.userId = initializer.config.id;
  }

  // Do something that relies on the configuration being initialized
  getUserId() {
    console.log("Is configuration initialized?", !!this.initializer.config.id)
    return this.userId;
  }
}

инициализатор.service.ts

@Injectable()
export class InitializerService {
  config = {};

  constructor(private http: HttpClient) { }

  initialize() {
    // TODO replace with link to a config file
    const url = "https://jsonplaceholder.typicode.com/users/1";
    return this.http.get(url).toPromise().then(response => {
      this.config = response;
      console.log("App initialized", this.config);
    }, e => console.error(e));
  }
}

app.module.ts

...
export function initializerServiceFactory(initializerService: InitializerService) {
  return () => initializerService.initialize();
}
...
providers: [
  InitializerService,
  InjectedDependencyService,
  {
    provide: APP_INITIALIZER,
    useFactory: initializerServiceFactory,
    deps: [InitializerService],
    multi: true
  },
  {
    provide: HTTP_INTERCEPTORS,
    useClass: InterceptorService,
    multi: true
  }
]
...

Результат

консольный вывод


person Oram    schedule 07.06.2019    source источник
comment
В своем перехватчике проверьте, загружен ли ваш конфиг, если нет, добавьте свой вызов API к подписчику, который будет ждать загрузки конфига.   -  person Florian    schedule 07.06.2019
comment
Вы должны вернуть действительное обещание из initializerService.initialize()   -  person isevcik    schedule 07.06.2019
comment
Я не уверен, получу ли я это. Но я думаю, если вы ставите перехватчик, то этот первый запрос на получение конфига пройдет через него, до получения самого конфига   -  person Doguita    schedule 07.06.2019
comment
@Florian, как предположил Догита, проблема в том, что перехватчик перехватывает вызов от initializerService, а затем создает экземпляры служб. Смотрите принятый ответ для решения.   -  person Oram    schedule 10.06.2019
comment
@isevcik, что не так с обещанием, созданным из Promise.all?   -  person Oram    schedule 10.06.2019


Ответы (1)


InitializerService вызывает HttpClient, который запускает InterceptorService, который, в свою очередь, вызывает getUserId(). Вы хотите, чтобы инициализация завершилась до того, как будут использованы какие-либо перехватчики.

Вы можете использовать HttpBackend для обхода всех перехватчиков.

@Injectable()
export class InitializerService {
  config = {};

  constructor(private backend: HttpBackend) { }

  initialize() {
    const http = new HttpClient(this.backend);
    const url = "https://jsonplaceholder.typicode.com/users/1";
    return http.get(url).toPromise().then(response => {
      this.config = response;
      console.log("App initialized", this.config);
    }, e => console.error(e));
  }
}
person Reactgular    schedule 07.06.2019