web-dev-qa-db-fra.com

Angular2 APP_INITIALIZER non cohérent

J'utilise APP_INITIALIZER comme il est recommandé dans this answer, mon service retournant une promesse, mais il n'attend pas toujours qu'elle soit résolue et je peux voir mon composant console.logging non défini, puis le service objet téléchargé. 

J'ai besoin de l'application pour ne rien faire avant que ces données soient chargées.

app.module.ts

import { NgModule, APP_INITIALIZER } from '@angular/core';
import { Http, HttpModule, JsonpModule } from '@angular/http';
import { UserService } from '../services/user.service';

<...>
@NgModule({
  imports: [
    BrowserModule,
    HttpModule,
    FormsModule,
    JsonpModule,
    routing
  ],
  declarations: [
    AppComponent,
    <...>
  ],
  providers: [
    <...>
    UserService,
    {provide: APP_INITIALIZER,
      useFactory: (userServ: UserService) => () => userServ.getUser(),
      deps: [UserService, Http],
      multi: true
    }
  ],
  bootstrap: [AppComponent]

user.service.ts

@Injectable()
export class UserService {

    public user: User;
    constructor(private http: Http) { }

    getUser(): Promise<User> {
        console.log('get user called');
        var observable= this.http.get('/auth/getuser', { headers: getHeaders() })
            .map(extractData);

        observable.subscribe(user => {this.user = user;
            console.log(this.user)});
        return observable.toPromise();
    }
}
11
LLL

Essayez le code suivant:

getUser(): Promise<User> {
    console.log('get user called');
    var promise = this.http.get('/auth/getuser', {headers: getHeaders()})
        .map(extractData)
        .toPromise();
    promise.then(user => {
        this.user = user;
        console.log(this.user);
    });
    return promise;
}

Je faisais face au même problème, et utiliser une promesse au lieu d’une observable me convenait.

17
Remidy

Je pense que le problème est dû au fait que vous vous abonnez à l'observable . Cela devrait fonctionner

@Injectable()
export class UserService {

    public user: User;
    constructor(private http: Http) { }

    getUser(): Promise<User> {
        console.log('get user called');
        return observable= this.http.get('/auth/getuser', { headers: getHeaders() })
            .map(extractData)
            .do(user => {
                this.user = user;
                console.log(this.user)
             })
            .toPromise();
    }
}

Je ne sais pas si toPromise() est nécessaire. Je m'attendrais à ce que cela fonctionne avec Observable également.

2
Günter Zöchbauer

Gardez votre itinéraire avec une classe CanActivate en utilisant une promesse qui charge les paramètres de configuration devrait également fonctionner.

Utilisez le service appSettings.service avec une fonction semblable à celle renvoyant une promesse

getAppSettings(): Promise<any> {
        var observable = this.http.get(this.ApiUrl, { headers: this.headers })
            .map((response: Response) => {
                var res = response.json();
                return res;
            });

        observable.subscribe(config => {
        this.config= config;
        console.log(this.config)
        });
        return observable.toPromise();  
    }

Et la garde CanActivate comme ci-dessous:

import { Injectable } from '@angular/core';
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { AppSettingsService } from './appsettings.service';

@Injectable()
export class CanActivateViaAuthGuard implements CanActivate {

//router: Router
    constructor(private appSettingsService: AppSettingsService)
    {
    }

    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
               return this.appSettingsService.getAppSettings().then(() => { 
    return true });
   }
}

Cela garantira que vos paramètres sont disponibles lors de la construction des composants correspondants. (utiliser APP_INITIALIZER ne limitait pas le constructeur appelé, je devais donc utiliser cette technique, assurez-vous également que vous n'exportez pas tous les composants des exportations: [] dans le module)

Pour protéger les itinéraires et vous assurer que les paramètres sont chargés avant l'appel des constructeurs, veuillez utiliser l'option habituelle canActivate dans le chemin d'accès à la définition de l'itinéraire.

 path: 'abc',
 component: AbcComponent,
 canActivate: [CanActivateViaAuthGuard]

L'initialisation de la configuration de l'application doit avoir lieu avant que le constructeur d'AbcComponent soit appelé, ceci est testé et fonctionne dans Angular 2.0.1 

Je ne suis pas sûr que ce soit le bon endroit pour charger la configuration mais semble servir l'objectif 

1
abhijoseph

En retard, mais si vous souhaitez que votre classe Service retourne des observables (appelez-la), appelez-la ainsi dans votre classe App Module:

function authenticationFactory(service: AuthenticationService) {
  console.log("calling login");
  //Call auth service login to get JWT info and startup data.
  //Also convert from an Observable to a Promise to work with APP_INITIALIZER.
  return () => service.login().toPromise().then(/*do nothing here*/);
}

NgModule Metadata stuff...

providers: [
   ...
    AuthenticationService,
    {
      provide: APP_INITIALIZER,
      useFactory: authenticationFactory,
      deps: [AuthenticationService],
      multi: true
    },
    ...
  ],
0
Chris Moore