inertiajs-tables-laravel-query-builder
inertiajs-tables-laravel-query-builder copied to clipboard
Pagination: changing page submits two requests
I was about to investigate why tables tend to flicker on page change and set up a very simple table.
My controller:
public function test(Request $request) {
$elements = QueryBuilder::for(Something::class)
->defaultSort('name')
->paginate(perPage: request('perPage')??25);
return Inertia::render('Something/Test', [
'elements' => $elements,
])->table(function (InertiaTable $table) {
$table
->perPageOptions([25, 50, 100, 200])
->defaultSort('name')
->column(key: 'name', label: 'Name');
});
}
My vue with table:
<script setup>
import AppLayout from '@/Test/Layouts/AppLayout.vue';
import { Table } from "@protonemedia/inertiajs-tables-laravel-query-builder";
const props = defineProps({
elements: Object,
});
</script>
<template>
<AppLayout>
<Table :resource="elements" />
</AppLayout>
</template>
By default it shows 25. When I select 50 as perPage, it shows me 50. When I now go to page 2, it triggers two calls, which retrieves data from DB, twice. Once with correct perPage parameter and once without.
- /test?page=2
- /test?page=2&perPage=50&sort=name
After digging a bit in code I found the problem in Table.vue providing function "function visit(url)" to Pagination.vue as onClick handler:
<slot
v-if="resourceMeta.total > queryBuilderProps.perPageOptions[0]"
name="**visit**"
:on-click="onSetPageChange"
:has-data="hasData"
:meta="resourceMeta"
:per-page-options="queryBuilderProps.perPageOptions"
:on-per-page-change="onPerPageChange"
>
<Pagination
:on-click="**visit**"
:has-data="hasData"
:meta="resourceMeta"
:per-page-options="queryBuilderProps.perPageOptions"
:on-per-page-change="onPerPageChange"
/>
</slot>
As soon as one of the numbers gets clicked, it calls visit, which calls the passed URL. the URL however is a static set of URLs, dot being modified when perPage or any filter gets updated. Instead urls of the following kind are called: https://myserver/test?page=2
In "visit" method, the mnodel is updated: queryBuilderData.value.page = queryBuilderProps.value.page
and this triggers
watch(queryBuilderData, () => {
visit(location.pathname + "?" + generateNewQueryString())
}, {deep: true})
which finally calls the second visit with the correct parameters, refreshing the page with correct values.
I am not too deep into this project though, so I open it as an issue and maybe someone finds a better solution. For me to avoid those problems, I created a "CustomizedTable.vue" and added a new method, which is called on page change and which only updates the model, thus calling visit implicitely, once, instead of calling visit also explicitely. function onSetPageChange(url) {
let pageName= $inertia.page.props.queryBuilderProps[props.name].pageName??'page';
let tmp = url.split('?');
if (tmp.length == 2) {
tmp = tmp[1];
} else {
tmp = "";
console.log("Expected URL to contain a single ?");
}
// Using Proxy class is fastest solution to date
// See https://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript
const params = new Proxy(new URLSearchParams(tmp), {
get: (searchParams, prop) => searchParams.get(prop),
});
let value = params[pageName];
queryBuilderData.value.cursor = null;
queryBuilderData.value.page = value;
}
I guess there are better ways to do this, I did not want to abuse the label, so maybe the whole data model should be extended. Then I passed this method to Pagination like:
<Pagination
:on-click="onSetPageChange"
In the end I still ended up with a second call (this time with identical parameters) as visit() changes the parameters which triggers the watch. As the parameters were equal, I just catched it with s stupid if statement in the onSuccess
method of visit():
if (queryBuilderData.value.cursor != queryBuilderProps.value.cursor) queryBuilderData.value.cursor = queryBuilderProps.value.cursor
if (queryBuilderData.value.page != queryBuilderProps.value.page) queryBuilderData.value.page = queryBuilderProps.value.page
This is not very clean code but did the job now and as I spend a LOT of hours today on debugging why my tables behave strangely, I will probably use this workaround unil there is a nicely done fix.
Hello,
Same thing here, default sort is append or params are reordered alphabetically.


Hi, I made a PR #126