web-dev-qa-db-ja.com

angularルーターガードを使用してngrx / storeをアタッチする方法

私はngrx/storeの初心者で、これを使用した最初のプロジェクトです。

angular ngrx/storeを使用したプロジェクトを正常に設定し、次のようにメインコンポーネントを初期化した後、loadアクションをディスパッチできます。

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を1度だけディスパッチする必要があるため、ルーターガードでこれを使用する方法を理解することはできません。

また、コンポーネントがLOADアクションをディスパッチする必要があること、初期データをロードすることは私には正しく聞こえません。ストア自体がデータをロードする必要があることを知っていて、最初にアクションをディスパッチする必要がないことを期待します。この場合は、コンポーネントのngOnInit()メソッドを削除できます。

私はすでにngrx-example-appを調べましたが、これがどのように機能するのか本当に理解していません。

編集:

Ngrx-storeオブザーバブルを返す解決ガードを追加した後、ルートがアクティブ化されません。これが解決策です:

   @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}},
];
12
Marco Rinck

AngularFranceからの貴重な情報を得て、自分で解決しました。私はまだ初心者なので、これがどのように行われるべきかはわかりませんが、うまくいきます。

CanActivate Guardを次のように実装しました。

@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を呼び出して、Observableをtrueに返します。

6
Marco Rinck

解決されたデータをリゾルバー経由でコンポーネントに送信する理由がよくわかりません。 ngrxストアをセットアップする全体のポイントは、単一のデータソースを持つことです。したがって、ここで単にやりたいことは、データが真であることを確認することです。残りは、コンポーネントはセレクターを使用してストアからデータを取得できます。

コンポーネントのngOnInitメソッドから_LOAD_ACTION_を呼び出していることがわかります。アクションを呼び出してデータを解決し、それがコンポーネントにつながるInitのアクションを呼び出すことはできません!これがルーターがルートを読み込まない理由です。

あなたがそれを理解したかどうかはわかりませんが、それは理にかなっています!

簡単に言えば、あなたがしていることはこれです。部屋をロックしてから、ドアの下の隙間からキーを押して、ドアが開かないのはなぜか疑問に思っています。

あなたと一緒に鍵を保管してください!

ガードのresolveメソッドは_LOAD_ACTION_を呼び出す必要があります。次に、解決はデータが読み込まれるのを待ってから、ルーターがコンポーネントに進む必要があります。

どうやってやるの?

他の答えはサブスクリプションを設定することですが、問題はあなたが警戒心でそれをしたくないことです。ガードをサブスクライブする必要があるため、ガードをサブスクライブすることはお勧めできませんが、データが解決されない場合、またはガードがfalseを返す場合、ルーターはサブスクライブを解除できず、混乱を招きます。

したがって、take(n)を使用します。この演算子は、サブスクリプションからn値を取得して、サブスクリプションを自動的に強制終了します。

また、アクションでは、_LOAD_STATISTICS_SUCCESS_と_LOAD_STATISTICS_FAIL_が必要です。サービスメソッドが失敗する可能性があるため!

レデューサーStateでは、loadedプロパティが必要です。これは、サービス呼び出しが成功し、_LOAD_STATISTICS_SUCCESS_アクションが呼び出されるとtrueになります。

メインのレデューサーgetStatisticsLoadedにセレクターを追加してから、ガードで、設定は次のようになります。

_resolve(): Observable<boolean> {

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

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

}

したがって、loadedプロパティがtrueに変更された場合にのみ、filterはストリームの続行を許可します。 takeは最初の値を取得して渡します。その時点で解決が完了し、ルーターは処理を続行できます。

繰り返しますが、takeはサブスクリプションを強制終了します。

7
notANerdDev

「ルーターガード」とは、resolveガードを意味していると思いますか。ルートをアクティブにする前にデータをロードしたいので?

はいの場合、すべてがうまく連携するはずです。

  • Ngrx/storeからの値は、オブザーバブルとして公開されています(たとえば、ドキュメントでstore.select('counter')にはオブザーバブルが含まれています)。
  • Angular resolve guards can can return observables

したがって、解決からngrx/storeオブザーバブルを単に返すことができます。

class MyResolver implements Resolve<any> {

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

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

そうは言っても、設定は少し複雑に見えます。私は状態を一時データ(メニューの折りたたまれた状態、データの持続期間中に複数の画面/コンポーネント間で共有する必要があるデータ)と考える傾向があります現在のユーザーなどのセッション)が、バックエンドから状態にデータの大きなスライスをロードするかどうかはわかりません。

統計を状態に保存することで何が得られますか?(対単にそれらをロードして、いくつかのコンポーネントに表示する)

4
AngularChef

ロードが成功しない場合は、おそらくナビゲーションをキャンセルする必要があります。エラーをスローすることで実行できます。最も重要なことは-返されたオブザーバブルが完了するか、エラーで終了する必要があることです。

resolve(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
): Observable<boolean> {

    this.store.dispatch(new LoadStatistic(route.params.someParam));

    return this.store.pipe(select(selectStatisticModel),
        filter(s => !s.isLoading),
        take(1),
        map(s => {
            if (!s.isLoadSuccess) {
                throw s.error;
            }
            return true;
        })
    );
}
2
Mārcis

この状況でngrx/effectを使用してイライラする人のためのこの回答。たぶん、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; 
    })  
}
0
alexKhymenko

これは失敗したリクエストも考慮します:

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
Martin Cremer