Reputation: 269
I am working on Angular project which uses NgRx to cache data. On a Submit button event, I need to fetch 20 records from server and display it. Now, I need to implement pagination(Next and Previous only) for next iteration. When I make a next request, I want to append the response to cache(NgRx store) so that I don't have to make request to backend for "Previous" page. In short, If cache already contains records, I don't want to make request to server. Where and how should I implement pagination logic? In a component or using Router Resolver?
My current implementation(not working) is:
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?! */);
}
My NgRx Effect looks like this:
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: []}))
)
)
)
)));
My service looks like this:
getAds(queryParams: QueryParams) {
console.log(queryParams);
return this.http.get(`${this.BASE_URL}/ads`, { params: { ...queryParams } });
}
I am calling getAds() method based on router query parameters change listener. What am I doing wrong? I would really appreciate if someone could help me on this. Thank you!
Edit: Added Effect and Service
Upvotes: 2
Views: 1867
Reputation: 10790
Well it will be complicated but i will try to describe.
- Change your state to save your
GuestAd
fromArray
toMap
export interface State {
... // other properties
guestAds : Map<string,Array<GuestAd>>;
}
- Change your reducer to handle map operations :
... // 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};
}
- Add another selector that accepts parameters
export const selectMappedGuestAds = createSelector(guestAds, (mappings, props) => mappings.get(JSON.stringify(props.payload));
- Modify effects to check if data available :
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: []}))
)
)
)
)));
- Finally change your store request into parametered one :
this.store.select(selectMappedGuestAds,{payload:queryParams});
Upvotes: 0