I'm struggling to implement server side pagination (standard paginated data table) using feathers-vuex. I currently bind the items of the data table to a computed property like this:
computed: {
items () {
// Return the items from the store
return this.findGetterForItems()
}
}
Next, I watch for changes in a pagination object (limit, skip, items per page). For each change, I clear the store by commiting clearAll and load the new data into the store using the find (from server) method.
Am I doing this correctly or is there a more "vuexish" way to do it? For questions like this, it would be nice to have a more "complete" example than the chat one...
If clearAll first, that will be a short time empty table in UI
And how do you deal with the total return from server for pagination? Currently map state it from pagination.default.total
I am interested in this one too. What is the recommended way yo query a paginated service
I am also interested. Rigth now I'm using a custom computed maping the ids from the pagination object. Something like that (Assuming you have a "products" service):
import { get } from "lodash";
// ...
computed: {
collection() {
const ids = get(this.$store.state, "products.pagination.default.ids", []);
return ids.map(id => this.$store.state.products.keyedById[id]);
}
}
//...
Here is the OFFICIAL article on pagination... it's a bit light,
https://feathers-vuex.feathers-plus.com/service-module.html#querying-with-find-pagination
I'm very happy with the results of my implementation. However I need to throw it into a mixin or something to generalize it. Making a demo out of this would be fun
in my data I have
data () {
return {
search: '',
loading: false,
scrapPagination: true
}
},
Then I check if a page is left.
...mapState('user', {
paginationFull: `pagination`
}),
pagination () {
return this.scrapPagination || this.paginationFull[NS]
},
isAPageLeft () {
if (!this.paginationFull[NS] || this.scrapPagination) {
return true
}
const loadedCount = this.paginationFull[NS].skip + this.paginationFull[NS].limit
return loadedCount < this.paginationFull[NS].total
}
Methods is where it gets nasty. One of the goals is to make the user mostly unaware of any loading occurring, as such I load two pages on every go. I use the 10th from last elements on screen presence as a trigger for loading more users. And I ignore pagination object if switching between regular listing and searching. Searching can be intense, so only do it if requested, as opposed to running the regex engine on everything for no reason.
methods: {
...mapActions('user', {
findUsers: 'find'
}),
...mapMutations('user', {
clearAll: 'clearAll'
}),
onScroll () {
const tolerance = 10
if (!this.$refs.userEls || !this.isAPageLeft) {
return
}
const triggerEl = this.$refs.userEls.length - tolerance > 0
? this.$refs.userEls[this.$refs.userEls.length - tolerance]
: this.$refs.moreButton
const that = this
requestAnimationFrame(() => {
if (isElementInViewport(triggerEl)) {
that.loadMore()
}
})
},
async loadMore (bypass) {
winston.debug(`${NS}: Loading More:`, this.pagination)
if (
bypass ||
(this.isAPageLeft && !this.loading)
) {
this.loading = true
winston.debug(`${NS}: Loading More:`, 'FIRE!')
const skip = this.scrapPagination ? 0 : this.pagination.skip + this.pagination.limit
this.scrapPagination = false
await this.findUsers({
qid: NS,
query: {
$limit: LIMIT,
$sort: SORT,
$skip: skip,
$search: this.search ? this.search : undefined
}
})
// Load two pages to begin with
if (this.isAPageLeft && !bypass) {
this.loadMore(true)
}
}
this.loading = false
},
reset () {
this.scrapPagination = true
this.clearAll()
},
init () {
this.reset()
this.loadMore()
}
},
watch: {
search: async function () {
if (!this.search) {
this.search = undefined
this.init()
return
}
this.init()
}
},
created () {
this.init()
window.addEventListener('scroll', throttle(this.onScroll, 300))
},
destroyed () {
window.removeEventListener('scroll', this.onScroll)
}
}
I used faker for the dummy data.
@daenuprobst, support for server-side pagination is now baked into the makeFindMixin by default in the 2.0 prerelease. It keeps track of which records return with each set of $limit and $skip params. This also means that the lists are not reactive anymore, even though the data in the lists continues to be reactive. You can set paginate:false in the params to get a reactive list, again.
Most helpful comment
I am interested in this one too. What is the recommended way yo query a paginated service