Búsqueda de sitios web

typescript - Angular: Cómo implementar correctamente APP_INITIALIZER


Tengo una aplicación Angular 5.2.0. Busqué cómo implementar APP_INITIALIZER para cargar información de configuración antes de que la aplicación se inicie. Aquí un extracto del app.module:

providers: [
    ConfigurationService,
    {
        provide: APP_INITIALIZER,
        useFactory: (configService: ConfigurationService) =>
            () => configService.loadConfigurationData(),
        deps: [ConfigurationService],
        multi: true
    }
],

Aquí está el configuration.service:

import { Injectable, Inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';

import { Configuration } from './configuration';

@Injectable()
export class ConfigurationService {
    private readonly configUrlPath: string = 'Home/Configuration';
    private configData: Configuration;

    constructor(
        private http: HttpClient,
        @Inject('BASE_URL') private originUrl: string) { }

    loadConfigurationData() {
        this.http
            .get<Configuration>(`${this.originUrl}${this.configUrlPath}`)
            .subscribe(result => {
                this.configData = {
                    test1ServiceUrl: result["test1ServiceUrl"],
                    test2ServiceUrl: result["test2ServiceUrl"]        
                }
            });
    }

    get config(): Configuration {
        return this.configData;
    }
}

Aquí hay un ejemplo de un constructor de un componente donde se utiliza configData:

export class TestComponent {
    public test1ServiceUrl: string;

    constructor(public configService: ConfigurationService) {
        this.test1ServiceUrl = this.configService.config.test1ServiceUrl;
    }
}

Funciona bien con todos los componentes que están definidos dentro del <router-outlet></router-outlet>. Pero la misma implementación en un componente fuera del <router-outlet></router-outlet> no funciona.
Cuando depuro el constructor respectivo del componente donde no funciona, dice que configService es null.
¿Por qué se ejecuta el APP_INITIALIZER antes de que se llame al constructor de un componente dentro del <router-outlet></router-outlet>, pero no antes del constructor de un componente fuera del <router-outlet></router-outlet>?

Respuesta #1

Debido a cómo funciona APP_INTIALIZER, se espera que los inicializadores asincrónicos devuelvan promesas, pero tu implementación del multiproveedor de APP_INTIALIZER no lo hace porque la función loadConfigurationData no devuelve nada.

Debería ser algo así:

loadConfigurationData(): Promise<Configuration> {
  return this.http.get<Configuration>(`${this.originUrl}${this.configUrlPath}`)
  .do(result => {
    this.configData = result;
  })
  .toPromise();
}

Respuesta #2

Haz algo como

 export function StartupServiceFactory(startupService: StartupService) {
      return () => startupService.load();
    }
    const APPINIT_PROVIDES = [
      StartupService,
      {
        provide: APP_INITIALIZER,
        useFactory: StartupServiceFactory,
        deps: [StartupService],
        multi: true,
      },
    ];

Servicio de inicio

load():Promise{
    return new Promise(resolve, reject){
        //load all your configuration 
         resolve(); 
    }

  }

Artículos relacionados