2017-02-28 88 views
8

我是一個初學者與ngrx /店,這是我第一個使用它的項目。如何附加ngrx /存儲角度路由器後衛

我已經成功地建立了我的角項目,NGRX /存儲和我能夠初始化我的主要成分這樣的後派遣荷載作用:

ngOnInit() { this.store.dispatch({type: LOAD_STATISTICS}); }

我已經設置了一個效果當這個動作被分派加載數據:

@Effect() 
loadStatistik = this.actions.ofType(LOAD_STATISTICS).switchMap(() => { 
    return this.myService.loadStatistics().map(response => { 
     return {type: NEW_STATISTICS, payload: response}; 
    }); 
}); 

我的減速器是這樣的:

reduce(oldstate: Statistics = initialStatistics, action: Action): Statistik { 
    switch (action.type) { 
     case LOAD_STATISTICS: 
      return oldstate; 
     case NEW_STATISTICS: 
      const newstate = new Statistics(action.payload); 

      return newstate; 
    .... 

雖然這個方法有效,但我無法理解如何在路由器防護中使用它,因爲我目前只需要分派一次LOAD_ACTION。

此外,組件必須分派LOAD操作,加載初始數據聽起來並不合適。我期望商店本身知道它需要加載數據,我不必先發送一個動作。如果是這種情況,我可以刪除組件中的ngOnInit()方法。

我已經看了一下ngrx-example-app,但是我還沒有真正理解它是如何工作的。

編輯:

增加一個後衛的決心,返回NGRX店觀察到的路由沒有得到激活後。這裏是決心:

@Injectable() 
    export class StatisticsResolver implements Resolve<Statistik> { 
    resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Statistik> { 
     // return Observable.of(new Statistik()); 
     return this.store.select("statistik").map(statistik => { 
     console.log("stats", statistik); 

     return statistik; 
    }); 
} 

這是路線:

const routes: Routes = [ 
    {path: '', component: TelefonanlageComponent, resolve: {statistik: TelefonieStatistikResolver}}, 
]; 

回答

5

我剛從AngularFrance獲得寶貴意見後自己解決了這個問題。由於我仍然是初學者,我不知道這是否應該如何完成,但它是有效的。

我實現了一個CanActivate保護這樣的:

@Injectable() 
export class TelefonieStatistikGuard implements CanActivate { 
    constructor(private store: Store<AppState>) {} 

    waitForDataToLoad(): Observable<Statistik> { 
     return this.store.select(state => state.statistik) 
      .filter(statistik => statistik && !statistik.empty); 
    } 

    canActivate(route: ActivatedRouteSnapshot): Observable<boolean> { 
     this.store.dispatch({type: LOAD_STATISTIK}); 

     return this.waitForDataToLoad() 
      .switchMap(() => { 
       return Observable.of(true); 
      }); 
     } 
    } 
} 

方法canActivate(...)是第一調度動作以加載數據。在waitForDataToLoad()中,我們過濾數據已經存在並且不是空的(我的業務邏輯的實現細節)。

之後返回true,我們調用switchMap返回true的Observable。

+1

如果統計數據爲空,它會一直等下去麼? –

+0

這是一個簡化版的真實代碼,因爲簡單。錯誤響應在這裏丟失 –

4

我被「路由器守護」你的意思是一個resolve後衛假設?正如你想要在之前加載數據激活路線?

如果是的話,那麼一切都應該很好地一起玩:從NGRX /存儲

  • 值公開爲觀測值(例如,在他們的文檔store.select('counter')包含可觀察到的)。
  • In Angular resolve衛兵can return observables

所以,你可以簡單地返回NGRX /存儲觀察到的從你的決心:

class MyResolver implements Resolve<any> { 

    constructor(private store: Store<any>) {} 

    resolve(): Observable<any> { 
    // Adapt code to your situation... 
    return this.store.select('statistics'); 
    } 
} 

話雖這麼說,您的設置似乎有點複雜。我傾向於將狀態視爲瞬態數據(菜單的合攏狀態,需要在會話期間共享多個屏幕/組件(例如當前用戶)的數據),但我不確定我會從後端加載大量數據到狀態中。

從存儲統計數據中獲得什麼?(vs.只需加載它們並在某些組件中顯示它們)

+0

是的我的意思是解決警衛。但是,當我只返回「選擇」Observable時,路線根本不會被激活。但我可以訂閱它並收到數據。我需要做一些特別的事情來「解決」它嗎? PS:這是一個簡單的演示項目,可以學習redux/ngrx,因此存儲統計信息的收益就是我學習的內容。 –

+0

路由器本身應該訂閱你的observable。在解決方案中,嘗試返回一個虛擬可觀察值,例如'return Observable.of(「foo」)',以確定它是否是ngrx可觀察值是問題所在,或者是否使用resolve來聲明路由。 – AngularChef

+0

是的,這確實有效。我剛剛使用解析器類編輯了我的問題。 –

6

我不太明白爲什麼要通過解析器將解析的數據發送到您的組件。建立一個ngrx商店的關鍵是要有一個單一的數據源。所以,你只是想在這裏做的是確保數據是真實的。其餘部分可以使用選擇器從商店獲取數據。

我可以看到您從組件的ngOnInit方法調用LOAD_ACTION。您無法調用某個操作來解析數據,然後導致該組件在您稱之爲操作的Init上!這就是爲什麼你的路由器沒有加載路由。

不知道你是否明白這一點,但它是有道理的!

簡而言之,你正在做的是這樣的。您正在鎖定一個房間,然後將鑰匙從門下方的空隙中推出,然後想知道爲什麼門沒有打開。

保持與你的關鍵!

警衛的解決方法應該調用LOAD_ACTION。然後解決方案應該等待數據加載,然後路由器應該繼續到組件。

你是怎麼做的?

其他答案是設置訂閱,但事情是你不想在你的看守那樣做。在後衛中訂閱並不是一個好習慣,因爲他們需要取消訂閱,但是如果數據沒有解決,或者後衛返回錯誤,那麼路由器永遠不會退訂,而且我們一團糟。因此,使用take(n)。該操作員將從訂閱中獲取n值,然後自動終止訂閱。

此外,在您的操作中,您將需要LOAD_STATISTICS_SUCCESSLOAD_STATISTICS_FAIL。由於您的服務方法可能會失敗!

在減速State,你會當服務調用成功,並LOAD_STATISTICS_SUCCESS動作被稱作需要loaded屬性,它變成true

在主減速器添加一個選擇,getStatisticsLoaded然後在你的gaurd,您的設置應該是這樣的:

resolve(): Observable<boolean> { 

this.store.dispatch({type: LOAD_STATISTICS}); 

return this.store.select(getStatisticsLoaded) 
    .filter(loaded => loaded) 
    .take(1); 

}

所以,只有當加載屬性更改爲true,過濾器將允許流繼續。 take將採取第一個值並傳遞。解決方案在此時完成,路由器可以繼續。

同樣,take將終止訂閱。

+1

謝謝,完美的回答!關鍵點:**在此時解決完成,路由器可以繼續。再次,採取將殺死訂閱。** –

+0

@notANerdDev所以在你得到的組件由商店的數據不是由路由器數據不是嗎?btw非常好的重播:) – Whisher

+1

@Whisher是的。這與使用商店的目的是一致的。 – notANerdDev

0

這也認爲失敗的請求:

canActivate(): Observable<boolean> { 
    this.store.dispatch({type: LOAD_STATISTICS); 

    return this.store.select((state) => state.statistics) 
     .filter((statistics) => statistics.loaded || statistics.loadingFailed) 
     .map((statistics) => statistics.loaded); 
    } 
0

這個答案的人誰感到沮喪使用NGRX在這種情況下/效果。也許更好地使用沒有ngrx/effects的簡單方法。

canActivate(route: ActivatedRouteSnapshot): Observable<boolean> { 
    // this will only dispatch actions to maybe clean up, NO EFFECTS 
    this.store.dispatch({type: LOAD_STATISTIK});  

    return this.service.loadOne() 
    .do((data) => { 
     this.store.dispatch({type: LoadSuccess, payload: data }) 
     }).map(() => { 
     return true; 
    }) 
}