Angular6 Использование полученной переменной конфигурации в модуле forRoot ()

В моем приложении мне нужно использовать ng-intercomhttps://github.com/CaliStyle/ng-intercom, который требует, чтобы ключ конфигурации был установлен в методе forRoot().

Ключ конфигурации загружается из вызова API, выполненного в main.ts. Конфигурация загружается из API, затем устанавливается на APP_CONFIG в AppConfigService, а затем приложение загружается.

main.ts

const appConfigRequest = new XMLHttpRequest();
appConfigRequest.addEventListener('load', onAppConfigLoad);
appConfigRequest.open('GET', 'https://api.myjson.com/bins/lf0ns');
appConfigRequest.send();

// Callback executed when app config JSON file has been loaded.
function onAppConfigLoad() {
    const config = JSON.parse(this.responseText);
    initConfig(config);
}

// Sets fetched config to AppConfigService constant APP_CONFIG and calls bootstrap()
function initConfig(config) {
    const injector = ReflectiveInjector.resolveAndCreate([AppConfigService]);
    const configService = injector.get(AppConfigService);
    configService.set(config);

    bootstrap();
}

function bootstrap() {
  platformBrowserDynamic().bootstrapModule(AppModule).then().catch(err => console.error(err));
}

app-config.service.ts

import { Injectable } from '@angular/core';

export let APP_CONFIG: any = {};

@Injectable()
export class AppConfigService {

    constructor() {}

    public set(config) {
        APP_CONFIG = Object.assign(config);
    }
}

В AppModule я импортирую APP_CONFIG и использую ключ из конфигурации в forRoot()IntercomModule:

app.module.ts

import { NgModule, InjectionToken, Inject, APP_INITIALIZER } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';

import { APP_CONFIG } from './../shared/services/app-config.service';
import { IntercomModule } from 'ng-intercom';

@NgModule({
  imports:      [ 
    BrowserModule, 
    FormsModule,
    IntercomModule.forRoot({
        appId : APP_CONFIG.intercomKey,
        updateOnRouterChange: false
    })
  ],
  declarations: [ AppComponent ],
  bootstrap:    [ AppComponent ]
})

export class AppModule { }

Пример конфигурации:

{
  "production": true,
  "envName": "staging",
  "apiBaseURL": "//api.staging.site.com/v1/",
  "intercomKey": "qz0b98qo"
}

Проблема: при инициализации IntercomModule ключ конфигурации не определен APP_CONFIG.intercomKey, потому что конфигурация еще не была получена из API.

Как мне убедиться, что конфигурация доступна при инициализации модулей? Любые идеи?

Раньше я пытался вызвать функцию в методе forRoot(), но при компиляции приложения с AOT получал ошибку Function calls are not supported in decorators.

См. Также stackblitz: https://stackblitz.com/edit/angular6-intercom-issue

Вы можете попробовать использовать async/await. Здесь - хороший пример того, как его использовать. Это должно помочь. Также рассмотрите возможность использования Angular HttpClient вместо XMLHttpRequest. HttpClient превратит ваш запрос в обещания, что упростит работу с async/await. Вы можете посмотреть справочник здесь.

ygorazevedo 28.09.2018 14:54

Спасибо, попробую. Я использовал XMLHttpRequest для поддержки IE11.

emery 28.09.2018 15:02

И похоже, что HttpClient нельзя использовать в main.ts stackoverflow.com/questions/46681396/…

emery 28.09.2018 15:11

Так как вам придется использовать XMLHttpRequest, здесь - хороший пример того, как работать с async/await и им.

ygorazevedo 28.09.2018 15:14

Использование обещаний, похоже, не решает мою проблему. Кажется, что Angular создает AppModule (строка 10) перед тем, как выполнить код, выполняющий запрос (строка 36). См. stackblitz.com/edit/…

emery 28.09.2018 15:28

Вы забыли использовать async/await. См. stackblitz.com/edit/angular6-intercom-issue-mmcjsj

ygorazevedo 28.09.2018 16:52

Думаю, я понимаю, что здесь происходит. Похоже, что AppModule загружается до того, как main.ts выполняет свои функции.

ygorazevedo 28.09.2018 16:59

Точно. Вы знаете, как этого избежать?

emery 28.09.2018 17:44
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Angular и React для вашего проекта веб-разработки?
Angular и React для вашего проекта веб-разработки?
Когда дело доходит до веб-разработки, выбор правильного front-end фреймворка имеет решающее значение. Angular и React - два самых популярных...
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Мы провели Twitter Space, обсудив несколько проблем, связанных с последними дополнениями в Angular. Также прошла Angular Tiny Conf с 25 докладами.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
Мое недавнее углубление в Angular
Мое недавнее углубление в Angular
Недавно я провел некоторое время, изучая фреймворк Angular, и я хотел поделиться своим опытом со всеми вами. Как человек, который любит глубоко...
Освоение Observables и Subjects в Rxjs:
Освоение Observables и Subjects в Rxjs:
Давайте начнем с основ и постепенно перейдем к более продвинутым концепциям в RxJS в Angular
0
8
1 847
2

Ответы 2

Проблема в том, что код в app.module.ts выполняется, как только вы импортируете его в свой main.ts. Вы должны динамически импортировать его для достижения своей цели. Я обновил ваш пример на Stackblitz, чтобы показать решение.

Однако вам, возможно, придется проверить, как TypeScript передает динамический импорт. Я не уверен, возможно ли в настоящее время ориентироваться на версию ES, которая широко поддерживается. См. Также документацию TypeScript по теме здесь.

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

Спасибо, мне пришлось обновить свой tsconfig.json с помощью "module": "esNext" и "angularCompilerOptions": { "entryModule" : "./app/app.module#AppModule" }, чтобы избежать ошибок при компиляции. Теперь он работает при обслуживании в режиме разработки, но когда я компилирую для prod с AOT, я получаю следующую ошибку: «Отклонение необработанного обещания: метаданные NgModule для 'e' не найдены»

emery 01.10.2018 15:18

У меня была аналогичная проблема, и ответ @realhans также сломал AOT для меня.

Упомянутое решение здесь решает проблему, пока AOT продолжает работать.

Другие вопросы по теме