Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NgRx store with pagination

I am trying to find out how to design the NgRx store when the server response is paginated.

if we keep only 1 page at the time in the store then what is the point of using NgRx at all?

if we keep slots for all pages and fill each page in the store on the first call for that specific page then how to handle page_size,... query params that can be different with each request.

How to deal with filtering and sorting in NgRx. is it worth keeping a response with too many query params?

my current interface is like

res : {
    pagination: {
        page: 1,
        page_size: 25,
        total_items: 10000,
        total_pages: 400
    },
    data: [array of 25 objects]
}
like image 879
ashil Avatar asked May 15 '20 06:05

ashil


2 Answers

Hi I will try to answer you question one by one, but you are on the right track

if we keep only 1 page at the time in the store then what is the point of using NgRx at all? The main reason to have the response in the store even if it's just for one page is to consume use that search result or part of it in an effect or another component or service, for example, if you have multi-row selection you will have a selector that returns the selected ones of the list you have in store and an action that processes them, another case is an export button to create a report, or is also good for caching so if your search is inside a tab, if you switch to another and come back you can collect all data from the store instead of executing it again.

It has many uses and in my experience it makes your life easier to have it in store, but yes you can perfectly have it as a local state in your component but in the times I have done it that way with the evolution of the app it can get complicated because more and more logic related to it goes in that component. One of the the main goals of ngrx is to split you code in pieces that are easier to extend, maintain and test, all the logic in the component can work but it's harder to maintain and test it all depending on how complicated your case is.

if we keep slots for all pages and fill each page in the store on the first call for that specific page then how to handle page_size,... query params that can be different with each request.

So if you want to cache all results, store them all in one array, and have a selector that returns the current page by slicing that big array where the start position is currentPage* pageSize and last position is the currentPage* pageSize + pageSize, every time the query params changes you must execute a new search and store it again, unless the search results are small enough and you can do an in-memory search, for which again it will be a selector that filters the main array depending on the query params

How to deal with filtering and sorting in NgRx. Is it worth keeping a response with too many query params?

So this depends on if the total set is small, then you can do it in memory, sorting if you are caching the entire result you can have like SortAction fired by the columns which change the main array, query params if in-memory have a selector that filters the main array.

Now if the search is too big generally is better for the search to be done by the backend and if they do the search they must also do the sort, so now any sort or query param change will trigger a new action that executes a search and the result is cache, here you can store one page or request 3 pages and use the slicing selector to return the pages you need or return a flag if the page is not in the cache so you can trigger another query.

Also depending on the pagination controls, you could implement a more clever caching, if you can jump x number of pages forward the way I described before is the best way, but if you have pagination where they can only go to next and previous like using a cdk virtual scroll then each time you get a result you could append it to the current result so going backward is all cache and forwards only 2 pages assuming you requested 3 pages.

In case you wonder why do I always mention caching 3 pages, there are studies that say that in paginated search generally more than 90% of the users don't go over the 3 page, but this could not apply to you. Also if you implement this several times, you will notice that some of the logic can be extracted to functions like sorting a field of an object in an array, or the slicing for the pages , I been planing to create something similar to ngrx entity but more focus to search sort and pagination but have not done it yet. Hope this helps.

like image 158
Gabriel Guerrero Avatar answered Oct 19 '22 19:10

Gabriel Guerrero


Not sure if there is a sense to store pageSize, we can get it from the number of objects of the current page. You can store the actual data of page identifiers in a separate property, and all the extracted data in a another property, and you can control the order elements using the keys property as you like. And after returning to the page with existing data, just update the date to sync.

You store

export interface State {
  page: number;
  totalPages: number;
  totalItems: number;
  keys: string[];
  data: {
    [id: string]: DataInterface;
  };
}

Get data with selector

export const getCurrentPageDataState = createSelector(getDataState, state => state.keys.map(key => state.data[key]);
like image 35
Валера Битковский Avatar answered Oct 19 '22 18:10

Валера Битковский