2016-09-27 63 views
10

我正在使用APP_INITIALIZER,就像在this的回答中推薦的那樣,我的服務返回了一個承諾,但它並不總是等待它解決,我可以看到我的組件console.logging undefined,然後服務記錄下載的對象。Angular2 APP_INITIALIZER不一致

我需要的應用程序在此數據加載之前不做任何事情。

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

它看起來OK。請說明您正在使用的軟件包版本。你是否能夠將問題複製爲小提琴? – estus

+0

我不確定你爲什麼獲取未定義值的日誌,但是如果你在observable上同時使用'toPromise()'和'subscribe()',你將會調用'/ auth/getuser'兩次。你應該鏈接rxjs運算符'share()'('rxjs/add/operator/share') –

回答

16

試試下面的代碼:

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

我面臨同樣的問題,並使用一個承諾,而不是一個可觀察的伎倆我。

+0

嘿,我確實在一段時間之前就開始工作了,但這正是我所做的。 – LLL

+0

我認爲observables是在對象的第一次變化時觸發的,它不包含所有的響應數據,而當整個任務完成時觸發promise。投票了,因爲這也解決了我的問題。 –

+0

@DesislavKamenov,不,它是「按設計」:)閱讀此主題:https://github.com/angular/angular/issues/9047 – rook

1

衛隊使用它加載的配置設置也應努力許諾一個CanActivate類路線。

使用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

我不確定它是否適合l OAD配置,但似乎服務於此目的

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一起使用。

0

晚,但如果你想保持你的服務類返回觀測量(我),這樣稱呼它在你的應用程序模塊類:

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 
    }, 
    ... 
    ],