如何使用Angular + NgRx实现“下一个”和“上一个”分页

时间:2019-12-21 16:49:51

标签: angular ngrx ngrx-store

我正在研究使用NgRx缓存数据的Angular项目。在Submit按钮事件中,我需要从服务器获取20条记录并显示它。现在,我需要为下一次迭代实现分页(仅适用于Next和Previous)。当我发出下一个请求时,我想将响应追加到缓存(NgRx存储),这样我就不必为“上一个”页面的后端发出请求。简而言之,如果缓存已经包含记录,则我不想向服务器发出请求。我应该在哪里以及如何实现分页逻辑?在组件中还是使用Router Resolver?

我当前的实现方式(无效)是:

constructor(
  private store: Store<AppState>,
  private activatedRoute: ActivatedRoute,
  private router: Router
  ) {
  this.guestAds$ = this.store.select(guestAds); 
  this.activatedRoute.queryParams.subscribe((params: any) => {
   if (Object.keys(params).length) {
     this.queryParams = params;
     this.getAds();
   }
 });
}

onSubmit() {
  const form = this.searchForm.value;
  const queryParams = {
    searchTerm: form.searchTerm,
    category: form.category,
    subCategory: form.subCategory,
    limit: 20,
    startAt: 0,
    prevKeys: ''
  };
  this.router.navigate(['/'], { queryParams });
}

getAds() {
  this.guestAds$
    .pipe(
      tap(ads => {
        if (ads.length) {
          ads.forEach(ad => {
            if (ad.createdAt === Number(this.queryParams.startAt)) {
              console.log('Data alreadyd available');
              /* some logic */
            } else {
              this.store.dispatch(LoadAds({payload: this.queryParams}));
            }
          });
        }
      }),
    ).subscribe(/* I dont know if this is required?! */);
}

我的NgRx效果如下:

loadAds$ = createEffect(() =>
  this.actions$.pipe(
    ofType(GuestAdsActions.LoadAds),
    mergeMap((action) => this.guestAdsService.getAds(action.payload)
      .pipe(
        map((payload: any) => payload.data),
        map((payload: Ad[]) => GuestAdsActions.LoadAdsSuccess({ payload }),
        catchError(() => of(GuestAdsActions.LoadAdsSuccess({ payload: []}))
        )
      )
    )
  )));

我的服务如下:

getAds(queryParams: QueryParams) {
  console.log(queryParams);
  return this.http.get(`${this.BASE_URL}/ads`, { params: { ...queryParams } });
}

我正在基于路由器查询参数更改侦听器调用getAds()方法。我究竟做错了什么?如果有人可以帮助我,我将不胜感激。谢谢!

编辑:增加了效果和服务

1 个答案:

答案 0 :(得分:0)

嗯,这很复杂,但是我会尝试描述。

  
      
  1. 更改状态,将GuestAdArray保存到Map
  2.   
export interface State {
... // other properties
guestAds : Map<string,Array<GuestAd>>;

}
  
      
  1. 更改减速器以处理地图操作:
  2.   
... // some other reducer cases
case GuestAdsActions.LoadAdsSuccess:
      const key = JSON.stringify(action.payload); // since your parameter object is complex better using string equality.
      if (state.guestAds.has(key )) { // if exists do not modify 
        return state;
      } else {
        const cloneSet = new Map(state.guestAds);
        cloneSet.set(key, action.data);
        return {...state, guestAds: cloneSet};
      }
  
      
  1. 添加另一个接受参数的选择器
  2.   
export const selectMappedGuestAds = createSelector(guestAds, (mappings, props) => mappings.get(JSON.stringify(props.payload));
  
      
  1. 修改效果以检查数据是否可用:
  2.   
loadAds$ = createEffect(() =>
  this.actions$.pipe(
    ofType(GuestAdsActions.LoadAds),
    withLatestFrom(this.store.select(guestAds), action,data=>{
     const key = JSON.stringify(action.payload); // again we need to stringify
     return data.has(key ) && data.get(key).length > 0 ? null : action;
}), // check store if has it? if has cached data return null 
fiter(action=> !!action), // if result not null then we need to request it from server    
    switchMap((action) => this.guestAdsService.getAds(action.payload)
      .pipe(
        map((payload: any) => payload.data),
        map((payload: Ad[]) => GuestAdsActions.LoadAdsSuccess({ payload }),
        catchError(() => of(GuestAdsActions.LoadAdsSuccess({ payload: []}))
        )
      )
    )
  )));
  
      
  1. 最后将您的商店请求更改为参数一:
  2.   
this.store.select(selectMappedGuestAds,{payload:queryParams});