Angular2 загрузка конфигурации из бэкэнд при запуске

В моем приложении Angular2 я пытаюсь загрузить конфигурации из бэкэнд на bootstrap с помощью HTTP, чтобы я мог использовать извлеченные данные для создания angular служб.

Всякий раз, когда я пытаюсь прочитать HTTP-ответ, сохраненный в моем Config, я всегда получаю TypeError: не могу прочитать свойство "url" ошибки undefined.

Похоже, что HTTP-запрос завершается только тогда, когда заканчивается весь метод начальной загрузки, тогда как мой код пытается извлечь ответ Observable до его получения.

Как я могу исправить его для извлечения конфигурации с сервера и использовать его для создания служб angular при запуске? (Я хочу создавать службы внутри провайдеров с извлеченными данными)

Прокомментируйте, если есть лучший способ извлечения конфигурации с сервера при запуске.

Мой main.ts выглядит следующим образом:

bootstrap(AppComponent, 
    [APP_ROUTER_PROVIDERS, HTTP_PROVIDERS, ConfigurationService, Config])
    .catch(err => console.error(err)
);

configuration.service.ts

@Injectable()
export class ConfigurationService {

    constructor(private http: Http) {
    }

    load() {
        return this.http.get('config/getConfig').map(response => response.json());
    }

}

config.ts

@Injectable()
export class Config {

    public config;

    constructor(public configurationService: ConfigurationService) {
        configurationService.load().subscribe(
            config => this.config = config,
            error => console.error('Error: ' + error)
        );
    }

    get(key: any) {
        return this.config[key];
    }
}

app.component.ts

@Component({
    selector: 'app',
    templateUrl: 'app/app.component.html',
    styleUrls: ['app/app.component.css'],
    directives: [ROUTER_DIRECTIVES],
    providers: [MyService]
})
export class AppComponent {

    constructor(public myService: MyService) {
    }

}

my.service.ts

@Injectable()
export class MyService{

    private url;

    constructor(public config: Config) {
        this.url = config.get('url');
    }
}

Ответ 1

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

bootstrap(AppComponent, [
  {
     provide: APP_INITIALIZER,
     useFactory: (config:Config) => {
       return config.load();
     },
     dependency: [ Config ]
   }
]);

Для этого вам нужно немного адаптировать ваш класс ConfigService:

@Injectable()
export class ConfigService {
  constructor(private http:Http) {}

  load() { // <------
    return new Promise((resolve) => {
      this.http.get(...).map(res=>res.json())
        .subscribe(config => {
          this.config = config;
          resolve();
        });
  }
}

Затем вы сможете напрямую получить доступ к объектам конфигурации в своем приложении.

Ответ 2

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

export function ConfigLoader(configService: ConfigService) {
    return () => configService.load();
}

и конфигурация для приложения выглядит так:

@NgModule({
    declarations: [
        AppComponent
    ],
    imports: [
        HttpModule,
        BrowserModule
    ],
    providers: [
        ConfigService,
        {
            provide: APP_INITIALIZER,
            useFactory: ConfigLoader,
            deps: [ConfigService]
        }],
    bootstrap: [ AppComponent ]
})
export class AppModule {
}

ссылка https://github.com/angular/angular/issues/10789

Ответ 3

Это лучший способ написать функцию загрузки

public load() {
    let promise =this.http.get(url).map(res => res.json()).toPromise();
    promise.then(config =>  this.validation = config);
    return promise;
};