I am not sure if i am missing something...
Just went through the example app and couldnt find an example/pattern for handling paginated collections (paginated by server).
If we imagine server API returns paginated collection and currently our store has 1st page with 10 entities (sorted alphabetically by the server by property name) all starting with letter a and now we want to create new entity with name zed.
Obviously, our newly created entity should not fall into collection we already have in store.
Same goes for update of entity thats not in state (for example from some search for single entity).
My point is, entity pattern seems to assume that, over time, we will have all entities in our store but i am sure i must be missing something about it.
So, is there some example or could someone share an idea how to incorporate ngrx/entity with paged data from server in some consistent way?
Related to #687
Maybe something like https://stackoverflow.com/a/48296722/1740331 could fit as a state structure.
Also remember that when removing rows you would have to remove item from pages it's currently on. Same goes when adding new rows.
Even Redux麓s Real world example https://github.com/reactjs/redux/blob/master/examples/real-world/src/actions/index.js#L102-L113 handles this by dispatching new page requests until there are no new pages. This isn't always an option when you really have lots of data.
Ionic used endless scrollable component to handle this back in Ionic 1. When virtual scrolled element hit the bottom there was this request more component which was placed in the bottom of the virtual scroll. When that component came into viewport it triggered an event to request more items. In this case pagination would be merely storing the possible next page request url / last loaded page number & page request loading state.
export interface TodoPaginationState {
// Use this in the table pagination if needed
currentPage: number;
// Number of available pages from backend
totalPages: number;
// Last page loaded (ok this doesn't allow sparse loading
// in that case you'd need an array of loaded pages)
lastLoadedPage: number;
// If a page is being loaded
pageLoading: boolean;
}
@MattiJarvinen-BA
Thanks for your post & examples. Just went through them.
Please correct me if i'm mistaken, but does ngrx/entity, in general, make these 2 assumptions:
1) That over time, all items will be loaded into store
2) That model in list is the same as model in details (as returned from server; in other words, that server always returns full DTO representation of model)
are those 2 true?
Also, how does ngrx/entity go together with multi-user apps where we have multi-user environment where multiple users could upsert same collection of items? Whenever one user updates something, he practically invalidates client side states for all other users. Usually, that's the reason why we have guards for involved components where in CanActivate we get fresh collection (just current page).
So, from all said, i have a feeling that ngrx/entity is good for single-user environments (or multi-user but with conflicting changes very unlikely) and for collection with very limited set of items.
Please correct me if i'm mistaken....am i on the right track?
thanks!
IMHO ngrx entity is 'just' a tool to handle basic CRUD actions on a entity collection.
You聽 don't need to have all your entities in state, but you can do so if you want. It really depends on your use case.聽
If you want a persistent state you should work with websockets. So that if your entity is changed on the sever, you can push these changes to every client or to a subset of clients. If it isn't business critical you could fetch the entities and update them in your client's state wherever/whenever you need, for example on a route change. To update or insert entities in batch you can use the upsert method.聽
For the pagination part you can extend your state with for example a current page index, number of records on a page and a total count of all records. With this set up you can then do the pagination in the selectors. To have this in ngrx entity would be IMHO not ideal because pagination could be done is several ways, depending on the use case.聽
If you want some help or feedback you should definitely check out the gitter channel.聽
My way to handle it is to create 2 reducer files for each entity: item.ts and item-pagination.ts
The first one works as usual and puts any received entity into store (always using upsert) while the second looks a bit as follow:
export interface Page {
key: number;
dataIds: number[];
query: ItemQuery;
meta: HeadersMeta;
}
export interface State extends EntityState<Page> {
currentDataIds: number[];
currentQuery: ItemQuery;
currentMeta: HeadersMeta;
loaded: boolean;
loading: boolean;
}
export const adapter: EntityAdapter<Page> = createEntityAdapter<Page>({
selectId: (page: Page) => page.key,
sortComparer: false
});
export const initialQuery: ItemQuery = {
page: 1,
'per-page': 20,
expand: 'brand',
fields: null,
sort: '-id',
filter: undefined
};
export const initialState: State = adapter.getInitialState({
currentDataIds: [],
currentQuery: initialQuery,
currentMeta: {},
loaded: false,
loading: false
});
Each time I do a request to server, I use a function to generate a key (or hash) from that ItemQuery and I use it to store the related response (only ids like: dataIds: [2, 5, 9, 10]) and meta data.
Then when I do the same request one more time, after generating its key, if I find I already have it in item-pagination.ts related store, I only load meta and dataIds from there and my selectors will use those ids to load or filter related entities from the first store (item.ts).
And of corse, that caching technique is useless if you don't have other services that knows about changes when they occur on server and properly update everything.
@tunecino can you please provide any link of your code.It would be really helpful.
@tunecino I agree with @SwatiSinha0693, if you could provide relevant code snippets that would be helpful
Hello guys, I have the same question posed by @codefactorydevelopment and I did it in my project in a way that worked for me:
https://github.com/peimelo/controlled-health-frontend/tree/master/src/app/weights
Most helpful comment
Related to #687
Maybe something like https://stackoverflow.com/a/48296722/1740331 could fit as a state structure.
Also remember that when removing rows you would have to remove item from pages it's currently on. Same goes when adding new rows.
Even Redux麓s Real world example https://github.com/reactjs/redux/blob/master/examples/real-world/src/actions/index.js#L102-L113 handles this by dispatching new page requests until there are no new pages. This isn't always an option when you really have lots of data.
Ionic used endless scrollable component to handle this back in Ionic 1. When virtual scrolled element hit the bottom there was this request more component which was placed in the bottom of the virtual scroll. When that component came into viewport it triggered an event to request more items. In this case pagination would be merely storing the possible next page request url / last loaded page number & page request loading state.