解决NGRX效果中的`switchMap`内部可观察的问题

时间:2019-08-09 21:32:57

标签: angular rxjs ngrx ngrx-effects

我正在使用NGRX缓存一些数据,如下所示:

@Effect() public getProductList$ = this.actions$
    .pipe(
        ofType<actionType>(actions.ACTION_NAME),
        withLatestFrom(this.store.select(x => x.storeSlice), (action: any, store: any) => store.data),
        switchMap((data: any) => {
            if (data.items.length > 0) {
                return [new actions.Success(data.items)];
            }

            const localData: any[] = localStorage.getItem(...);

            if (localData != null) {
                return [new actions.SuccessAction(localData)];
            }
            else {
                return this.apiService.getAll()
                    .pipe(
                        map(result => new actions.Success(result)),
                        catchError(error => actions.Failure(error));
            }
        })
    );

基本上,我尝试首先从商店中获取数据,如果结果数组中有项目,我将对数据返回成功操作。否则,我尝试从localStorage加载数据,如果存在,我将返回成功操作。最后,如果没有缓存的数据,我将调用API来获取数据,这可能会导致成功或失败操作。

问题在于数据增长太多,我无法再将其存储在localStorage中,所以我需要使用IndexedDb,它可以实现诺言。

我在IndexedDb api周围创建了包装服务,并且我的方法返回了带有数据的Observable。因此,我需要通过服务电话来切换const localData: any[] = localStorage.getItem(...);。使用localStorage可以使一切正常,因为它是同步的,但是IndexedDb却不是。

我尝试了类似的方法,但是它不起作用:

@Effect() public getProductList$ = this.actions$
    .pipe(
        ofType<actionType>(actions.ACTION_NAME),
        withLatestFrom(this.store.select(x => x.storeSlice), (action: any, store: any) => store.data),
        switchMap((data: any) => {
            if (data.items.length > 0) {
                return [new actions.Success(data.items)];
            }

            return this.idb.getData().map(localData => {
                if (localData != null) {
                    return [new actions.SuccessAction(localData)];
                }
                else {
                    return this.apiService.getAll()
                        .pipe(
                            map(result => new actions.Success(result)),
                            catchError(error => actions.Failure(error));
                }
            });


        })
    );

这不起作用,因为我返回的是Observable而不是实际的action。但是,如何从Observable中提取数据?是否有一个rxjs运算符可以帮助我解决这个问题?


如果将来有人来这里,这就是解决方案:

@Effect() public getProductList$ = this.actions$
    .pipe(
        ofType<actionType>(actions.ACTION_NAME),
        withLatestFrom(this.store.select(x => x.storeSlice), (action: any, store: any) => store.data),
        switchMap((data: any) => {
            if (data.items.length > 0) {
                return [new actions.Success(data.items)];
            }

            return this.idb.getData()
                .pipe(
                    map(data => new actions.SuccessAction(data),
                    switchMap(successActionFromIDB => {
                        if (successActionFromIDB.payload.length > 0) {
                            return of(successActionFromIDB);
                        }
                        else {
                            return this.apiService.getAll()
                                .pipe(
                                    tap(data => this.idb.setData(data)),
                                    map(data => new actions.SuccessAction(data)),
                                    catchError(error => new actions.FailureAction(error))
                                )
                        }
                    })
                )
            );
        })
    );

1 个答案:

答案 0 :(得分:1)

您可以这样做:

@Effect() public getProductList$ = this.actions$
    .pipe(
        ofType<actionType>(actions.ACTION_NAME),
        withLatestFrom(this.store.select(x => x.storeSlice), (action: any, store: any) => store.data),
        switchMap((data: any) => {
            // Return value as an observable with of()
            if (data.items.length > 0) {
                return of([new actions.Success(data.items)]);
            }

            const localData: any[] = localStorage.getItem(...);

            if (localData != null) {
                // Return value as an observable with of()
                return of([new actions.SuccessAction(localData)]);
            }

            // Return value as an observable with 
            return this.apiService.getAll()
                .pipe(
                    map(result => [new actions.Success(result) as any]),
                    catchError(error => [actions.Failure(error) as any])
                );
        }),
        // Flat map the observable result from the switchMap. This will flatten 
        // your result to an actual value.
        flatMap(data => data)
    );