Laravel + Inertia + Vuejs: Pagination
Asked Answered
E

3

20

When I get all the records it works:

    ...

    $items = Item::all();

    return Inertia::render('Rentals/Items', ['items' => $items]
);

But when I try to paginate, it breaks down:

    ...

    $items = Item::paginate(15);

    return Inertia::render('Rentals/Items', ['items' => $items]
);

I get this error:

Uncaught (in promise) TypeError: Cannot read property 'id' of null

Help please. What am I doing wrong? I've been stuck for days.

Enjambment answered 28/3, 2021 at 20:56 Comment(1)
Standard pagination with Laravel wont work with Inertia.js. Does one of the answers here may help you?: #61076583 – Enzymolysis
F
72

Creator of Inertia.js here. πŸ‘‹

So, you can totally use the Laravel paginator with Inertia, you just need to setup your page components to be compatible.

First, make sure you're only returning the items data to the client that you actually need. You can use the new pagination through method for this.

$items = Item::paginate(15)->through(function ($item) {
    return [
        'id' => $item->id,
        'name' => $item->name,
        // etc
    ];
});

return Inertia::render('Rentals/Items', ['items' => $items]);

Next, client side, you'll need a pagination component to actually display the pagination links. Here is an example component from the Ping CRM demo app, built using Tailwind CSS.

<template>
  <div v-if="links.length > 3">
    <div class="flex flex-wrap -mb-1">
      <template v-for="(link, key) in links">
        <div v-if="link.url === null" :key="key" class="mr-1 mb-1 px-4 py-3 text-sm leading-4 text-gray-400 border rounded" v-html="link.label" />
        <inertia-link v-else :key="key" class="mr-1 mb-1 px-4 py-3 text-sm leading-4 border rounded hover:bg-white focus:border-indigo-500 focus:text-indigo-500" :class="{ 'bg-white': link.active }" :href="link.url" v-html="link.label" />
      </template>
    </div>
  </div>
</template>

<script>
import {InertiaLink} from "@inertiajs/inertia-vue3";

export default {
  props: {
    links: Array,
  },
  components : {
    InertiaLink
  }
}
</script>

Finally, to display the items and the pagination links in your page component, use the items.data and items.links props. Something like this:

<template>
  <div>
    <div v-for="item in items.data" :key="item.id">
      {{ item.name }}
    </div>
    <pagination :links="items.links" />
  </div>
</template>

<script>
import Pagination from '@/Shared/Pagination'

export default {
  components: {
    Pagination,
  },
  props: {
    items: Object,
  },
}
</script>

You can find a full working example of this in the Ping CRM demo app. πŸ‘

Forbore answered 3/4, 2021 at 13:3 Comment(2)
Hi @Forbore with your code, when i'm compile pagination component i got VueCompilerError: v-if/else branches must use unique keys. I'm using vue 3 – Midwinter
@AdiMadhava Place the :key="key" attribute on the same element as the v-for instead of its children. – Callup
S
6

Just to keep this post updated I'm adding what works for me using vue 3, laravel 8 and inertia 0.10:

In Pagination component I must change the :key attribute just as @AdiMadhava said before, in addition I must use Link component in order to make the page links usables, so my entire @/Shared/Pagination.vue looks like:

<template>
    <div v-if="links.length > 3">
        <div class="flex flex-wrap mt-8">
            <template v-for="(link, key) in links" :key="key">
                <div
                    v-if="link.url === null"
                    class="mr-1 mb-1 px-4 py-3 text-sm leading-4 text-gray-400 border rounded"
                    v-html="link.label"
                />

                <Link
                    v-else
                    class="mr-1 mb-1 px-4 py-3 text-sm leading-4 border rounded hover:bg-white focus:border-primary focus:text-primary"
                    :class="{ 'bg-white': link.active }"
                    :href="link.url"
                    v-html="link.label"
                />
            </template>
        </div>
    </div>
</template>

<script>
import { defineComponent } from "vue";
import { Link } from "@inertiajs/inertia-vue3";
export default defineComponent({
    components: {
        Link,
    },
    props: {
        links: Array,
    },
});
</script>

And it works accurately.

Stonechat answered 19/1, 2022 at 15:36 Comment(0)
F
0

There is nothing wrong with your backend approach:

$items = Item::paginate(15);
return Inertia::render('Rentals/Items', ['items' => $items]

From the error message you got, it's clear the issue is about how you accessed the data on the frontend.

The id column you tried to access is not available in items object, but in items.data array.

So your v-for directive should loop over items.data, not items. That way, you can get the item's id.

Note that, on the frontend, you will need to declare items as an Object prop, not an Array prop.

In the same way, you can get access to the pagination data and links through items.links. Here is an example:

// import { Link } from "@inertiajs/vue3";
<div class="px-1 pt-10">
  <Link
    v-for=" (link, index) in items.links" 
    :key="index" 
    :href="link.url"
    class="px-1 border-2"
    v-html="link.label"
  />
</div>
Fletafletch answered 14/12, 2023 at 10:16 Comment(0)

© 2022 - 2025 β€” McMap. All rights reserved.