Angular2 APP_INITIALIZER несовместим

Я использую APP_INITIALIZER, как это рекомендуется в этом, с моей службой, возвращающей обещание, но она не всегда ждет, пока она разрешится, и я могу см. мой компонент console.logging undefined, а затем служба регистрирует загруженный объект.

Мне нужно приложение, чтобы ничего не делать до загрузки этих данных.

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();
    }
}

Ответ 1

Попробуйте использовать следующий код:

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;
}

Я столкнулся с той же проблемой, и использование обещания вместо наблюдаемого помогло.

Ответ 2

Я думаю, проблема вызвана тем, что вы подписываетесь на наблюдаемое. Это должно работать

@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();
    }
}

Я не уверен, что требуется toPromise(). Я ожидаю, что он будет работать и с Observable.

Ответ 3

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

Используйте приложение appSettings.service с функцией, аналогичной функции, возвращающей обещание

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();  
    }

И защита CanActivate, как показано ниже:

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 });
   }
}

Это обеспечит доступность ваших настроек при создании соответствующих компонентов. (использование APP_INITIALIZER не ограничивало вызывающий конструктор, поэтому мне пришлось использовать эту технику. Также убедитесь, что вы не экспортируете все компоненты в экспорт: [] в модуле)

Чтобы защитить маршруты и обеспечить загрузку параметров перед вызовом конструкторов, используйте обычную опцию canActivate в пути определения маршрута

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

Инициализация настроек appsettings должна произойти до того, как вызывается конструктор для AbcComponent, это проверено и работает в Angular 2.0.1

Я не уверен, что это подходящее место для загрузки конфигурации, но, похоже, служит цели

Ответ 4

Поздно, но если вы хотите, чтобы ваш класс обслуживания возвращал Observables (я делаю), вызовите его так же, как в классе 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
    },
    ...
  ],