Espero que Angular espere hasta que se resuelva mi función loadConfig() antes de construir otros servicios, pero no es así.
aplicación.módulo.ts
export function initializeConfig(config: AppConfig){ return () => config.loadConfig(); } @NgModule({ declarations: [...] providers: [ AppConfig, { provide: APP_INITIALIZER, useFactory: initializeConfig, deps: [AppConfig], multi: true } ] }) export class AppModule {}
aplicación.config.ts
@Injectable() export class AppConfig { config: any; constructor( private injector: Injector ){ } public loadConfig() { const http = this.injector.get(HttpClient); return new Promise((resolve, reject) => { http.get('http://mycoolapp.com/env') .map((res) => res ) .catch((err) => { console.log("ERROR getting config data", err ); resolve(true); return Observable.throw(err || 'Server error while getting environment'); }) .subscribe( (configData) => { console.log("configData: ", configData); this.config = configData; resolve(true); }); }); } }algún-otro-servicio.ts
@Injectable() export class SomeOtherService { constructor( private appConfig: AppConfig ) { console.log("This is getting called before appConfig's loadConfig method is resolved!"); } } Se llama al constructor de SomeOtherService antes de que se reciban los datos del servidor. Esto es un problema porque los campos en SomeOtherService no se establecen en sus valores adecuados.
¿Cómo me aseguro de que se llame al constructor de SomeOtherService solo DESPUÉS de que se resuelva la solicitud de loadConfig ?
También tuve un problema similar, lo que resolvió el problema para mí fue usar métodos y operadores observables para hacer todo. Luego, al final, solo use el método toPromise del Observable para devolver una Promise . Esto también es más simple porque no necesita crear una promesa usted mismo.
El servicio AppConfig se verá así:
import { Injectable, Injector } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs/Observable'; import { tap } from 'rxjs/operators/tap'; @Injectable() export class AppConfig { config: any = null; constructor( private injector: Injector ){ } public loadConfig() { const http = this.injector.get(HttpClient); return http.get('https://jsonplaceholder.typicode.com/posts/1').pipe( tap((returnedConfig) => this.config = returnedConfig) ).toPromise(); //return from([1]).toPromise(); } } Estoy usando los nuevos operadores pipeables en rxjs recomendados por Google para Angular 5. El operador tap es equivalente al antiguo operador do .
También he creado una muestra de trabajo en stackblitz.com para que pueda ver cómo funciona. enlace de muestra
async loadConfig() { const http = this.injector.get(HttpClient); const configData = await http.get('http://mycoolapp.com/env') .map((res: Response) => { return res.json(); }).catch((err: any) => { return Observable.throw(err); }).toPromise(); this.config = configData; }); }El operador await se usa para esperar una Promesa. Solo se puede usar dentro de una función asíncrona.
Está funcionando bien.
Injector no espera observables o promesas y no hay código que pueda hacer que suceda.
Debe usar Guard o Resolver personalizado para asegurarse de que la configuración se cargue antes de que se complete la navegación inicial.