I am coding a table with pagination component, and I use multiple v-model and watch
on these variables to fetch the data.
When perPage
is updated, I want to reset page
to 1. So I did it in my watch
method but of course, watch
is triggered twice (for perPage
and then page
).
Is it possible to update the variable and disable watch at this moment ?
Here is my current code:
<script setup lang="ts">
const sort = ref(route.query.sort || 'created_at')
const filters = ref(route.query.filters || {})
const page = ref(route.query.page ? parseInt(route.query.page.toString()) : 1)
const perPage = ref(route.query.per_page ? parseInt(route.query.per_page.toString()) : 10)
watch([sort, filters, page, perPage], ([oldSort, oldFilters, oldPage, oldPerPage], [newSort, newFilters, newPage, newPerPage]) => {
if (oldPerPage !== newPerPage)
page.value = 1
fetchItems()
router.push({
query: {
...route.query,
sort: sort.value,
// filters: filters.value,
page: page.value,
per_page: perPage.value,
},
})
})
async function fetchItems() {
items.value = await userApi.list({
filters: toRaw(filters.value),
sort: sort.value,
page: page.value,
perPage: perPage.value,
})
}
</script>
<template>
<CTable
:pagination-enabled="true"
v-model:sort="sort"
v-model:page="page"
v-model:per-page="perPage"
:total-items="items.meta.total"
:total-pages="items.meta.last_page"
/>
</template>
The only workaround I found is to return when I reset page
:
watch(..., () => {
if (oldPerPage !== newPerPage) {
page.value = 1
return
}
fetchItems()
...
})
It is working in my case but for some another cases I would like to update without trigger the watch method.
Thanks!
CodePudding user response:
Create another watcher for perPage
:
watch([sort, filters, page, perPage], ([oldSort, oldFilters, oldPage, oldPerPage], [newSort, newFilters, newPage, newPerPage]) => {
fetchItems()
router.push({
query: {
...route.query,
sort: sort.value,
// filters: filters.value,
page: page.value,
per_page: perPage.value,
},
})
})
watch(perPage, (newPerPage,oldPerPage ) => {
if (oldPerPage !== newPerPage)
page.value = 1
})
It's recommended to create watch for a single property separately to avoid unnecessarily updates and conflicts. For the first watch try to replace it with watchEffect
like since you're not using the old/new value:
watchEffect(() => {
fetchItems()
router.push({
query: {
...route.query,
sort: sort.value,
// filters: filters.value,
page: page.value,
per_page: perPage.value,
},
})
})
CodePudding user response:
Considering that the state is changed through v-model
, it needs to be observed with a watcher.
In order to avoid a watcher to be triggered multiple times, it should implement additional conditions like shown in the question, but it also needs to not skip an update when page
is already 1
, this won't result in additional update:
if (oldPerPage !== newPerPage) {
page.value = 1
if (newPage !== 1)
return
}
Otherwise there need to be multiple watchers, like another answer suggests. In case a watcher that causes asynchronous side effects (fetchItems
) shouldn't be triggered more than it needs, and there are other causes for this to happen besides perPage
, it can be debounced, e.g.:
watch(perPage, () => {
page.value = 1
});
watch([sort, filters, page, perPage], debounce(() => {
fetchItems()
...
}, 100));