Skip to content
This repository was archived by the owner on Jun 27, 2024. It is now read-only.
This repository was archived by the owner on Jun 27, 2024. It is now read-only.

Pagination: changing page submits two requests #119

Open
@mbeckerle-xqueue

Description

@mbeckerle-xqueue

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.

image

  1. /test?page=2
  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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions