Angular 2 Установите APP_BASE_HREF со значением из Promise/Observable

Я пытаюсь установить APP_BASE_HREF в «CoreModule» со значением из асинхронного остаточного вызова. Я не понимаю, как это делается, потому что метод provider должен возвращать строку.

Например:

@NgModule({
    imports: [
        ...
        HttpModule
    ],
    ...
    providers: [
        ...
        ...
        BackendRequestClass,
        { provide: APP_BASE_HREF, useFactory: () => () => return '/some/path', deps: [], multi: true }
    ],
});

но когда мне нужно значение из веб-службы, я не могу вернуть строку. Любые идеи, как это можно сделать?

спасибо


person Kevin Clerc    schedule 13.09.2017    source источник


Ответы (3)


Я попробовал ваше решение. Проблема в том, что в то время

{ provide: APP_BASE_HREF, useFactory: (config) => config.appBaseHref, deps: [ConfigService] }

config.appBaseHref еще не установлен. Когда я отлаживаю код, я вижу, что APP_INITIALIZER выполняется после провайдера из APP_BASE_HREF

Это приводит к тому, что BASE_HREF не установлен.

person Kevin Clerc    schedule 13.09.2017

У нас была та же задача: установить APP_BASE_HREF динамически на основе результата вызова асинхронного API, чтобы разрешить маршрутизацию приложения, к которому можно получить доступ через разные URL-адреса.

Хотя решение, представленное Гюнтером, выглядит мило, к сожалению, оно не сработало, по крайней мере, с Angular 7. В нашем случае APP_BASE_HREF всегда имел приоритет над APP_INITIALIZER, поэтому мы не могли инициализировать APP_BASE_HREF на основе разрешенного значения обещания, возвращаемого из инициализатора.

Вместо этого я реализовал вызов API перед начальной загрузкой Angular, а затем внедрил поставщика, чтобы он был доступен в контексте Angular.

Добавление к main.ts:

fetchConfig().then(config => {
  platformBrowserDynamic([ { provide: ConfigData, useValue: config } ])
    .bootstrapModule(AppModule)
    .catch(err => console.log(err));
});

Затем мы можем настроить APP_BASE_HREF в providers внутри app.module.ts:

providers: [
  // …
  { 
    provide: APP_BASE_HREF, 
    useFactory: (config: ConfigData) => config.base, 
    deps: [ ConfigData ] 
  }
]

[edit 2019-08-21] Уточнение согласно комментариям: fetchConfig() — это запрос API, который получает конфигурацию и возвращает Promise. И ConfigData — это не служба Angular, а просто простой класс, экземпляр которого создается в fetchConfig() и который обеспечивает безопасность типов при доступе к нему позже в моем приложении:

function fetchConfig (): Promise<ConfigData> {
  return fetch('/api/config')
    .then(response => response.json())
    .then(response => new ConfigData(response));
}
person qqilihq    schedule 23.07.2019
comment
Вместо того, чтобы выходить за пределы Angular, вы можете использовать механизм PLATFORM_INITIALIZER для получения конфигурации. Это срабатывает хорошо и рано (конечно, до APP_INITIALIZER). platformBrowserDynamic([{ provide: PLATFORM_INITIALIZER, useValue: fetchConfig, multi: true }]).bootstrapModule(AppModule) - person Gary McGill; 24.07.2019
comment
@GaryMcGill Спасибо за вклад - я попробую, когда у меня будет несколько свободных минут! - person qqilihq; 25.07.2019
comment
@qqilihq могу ли я увидеть код для fetchConfig()? Где вы определяете конфигурацию в main.ts? - person Arnold.Krumins; 16.08.2019
comment
@ Arnold.Krumins Это просто HTTP-запрос, который извлекает конфигурацию и преобразуется в обещание. - person qqilihq; 20.08.2019
comment
@qqilihq хорошо, спасибо. ConfigData — это служба? Если да, то как вы его создали в main.ts? - person Arnold.Krumins; 21.08.2019
comment
Нет, не сервис. Это просто класс. - person qqilihq; 21.08.2019
comment
@qqilihq Понятно. Так как же app.module.ts узнает об этом классе? Это статично? - person Arnold.Krumins; 21.08.2019
comment
@GaryMcGill Еще раз спасибо за предложение от июля. На самом деле у меня просто было свободное время, чтобы попробовать. К сожалению, PLATFORM_INITIALIZER, похоже, не поддерживает «ожидание» обещания (которое было бы необходимо для ожидания fetchConfig()). Что-то я пропустил или неправильно понял здесь? - person qqilihq; 08.11.2019

Вы можете использовать APP_INITIALIZER, чтобы получить путь заранее, а затем использовать зависимость, как показано в Angularjs2 - предварительная загрузка конфигурации сервера перед запуском приложения

export function loadConfig(config: ConfigService) => () => config.load()

@NgModule({
    declarations: [AppComponent],
    imports: [BrowserModule,
        routes,
        FormsModule,
        HttpModule],
    providers: [
        ConfigService,
        BackendRequestClass,
        { provide: APP_INITIALIZER,
          useFactory: loadConfig,
          deps: [ConfigService], 
          multi: true },
        { provide: APP_BASE_HREF, useFactory: (config) => config.appBaseHref, deps: [ConfigService] }

    ],
    bootstrap: [AppComponent]
})
export class AppModule { }

ConfigService может внедрить BackendRequestClass или Http и получить данные, а затем сделать их доступными, используя свое свойство appBaseHref (просто пример того, как это можно сделать).

person Günter Zöchbauer    schedule 13.09.2017
comment
Теоретически это выглядит как хорошее решение, однако APP_BASE_HREF, похоже, выполняется до APP_INITIALIZER (по крайней мере, в более новых версиях Angular — я использую 7). Это изменилось, или я что-то упускаю? - person qqilihq; 22.07.2019
comment
Я давно не работал с Angular и тоже не знаю. - person Günter Zöchbauer; 22.07.2019