Laravel have a pagination feature. It's help blade template to generate a pagination links. This feature is really useful for blade template.
But how do we display it in Javascript framework?
Here is example we can use in Vue.js framework. There is 2 method development for developer to use Vue.js framework.
I will show example for both.
Laravel Breeze (Inertia.js with Vue.js)
First, create a backend that return the pagination.
<?php
namespace App\Http\Controllers;
use App\Models\Activity;
use Inertia\Inertia;
class ActivityController extends Controller
{
public function index()
{
$activities = Activity::paginate(10)->withQueryString();
return Inertia::render('/activities/index', [
'activities' => $activities,
]);
}
}
In page file, we can use the component like this:
<script setup>
import PaginationBar from '../components/PaginationBar.vue'
const props = defineProps({
activities: Object,
});
</script>
<template>
<PaginationBar :links="props.activities.links" />
</template>
We create a pagination component file (resources/js/components/PaginationBar.vue
).
<script setup>
import { Link } from "@inertiajs/vue3";
import { onMounted, ref, watch } from "vue";
const props = defineProps({
links: Array,
});
const buttons = ref([]);
const button_prev_url = ref(null);
const button_next_url = ref(null);
const init = (limit) => {
let links = props.links;
let selected_index = null;
// remove prev & next
links.shift();
links.pop();
// select index
links.map((link, i) => {
if (link.active) selected_index = i;
});
button_prev_url.value = links[selected_index - 1]?.url;
button_next_url.value = links[selected_index + 1]?.url;
buttons.value = links;
};
watch(
() => props.links,
() => init(),
);
onMounted(() => init());
</script>
<template>
<div v-if="buttons.length > 1">
<div class="flex flex-wrap justify-center">
<Link
class="px-4 py-2 ml-2 mr-4 text-sm rounded-lg"
:class="
button_prev_url
? 'text-slate-500 focus:text-main-500 hover:bg-main-50 font-bold'
: 'text-slate-300'
"
:href="button_prev_url"
>
Previous
</Link>
<template v-for="(btn, i) in buttons" v-bind:key="i">
<Link
class="px-4 py-2 mx-0.5 text-sm font-bold rounded-lg hover:bg-main-50 focus:text-main-500"
:class="
btn.active ? 'bg-main-600 text-white' : 'text-slate-500'
"
:href="btn.url"
>
{{ btn.label }}
</Link>
</template>
<Link
class="px-4 py-2 ml-4 mr-2 text-sm rounded-lg"
:class="
button_next_url
? 'text-slate-500 focus:text-main-500 hover:bg-main-50 font-bold'
: 'text-slate-300'
"
:href="button_next_url"
>
Next
</Link>
</div>
</div>
</template>
External Vue.js Framework
For external, it's a bit challenging because Laravel pagination passing the backend url instead of frontend url.
Let's try create an API that return the pagination.
<?php
namespace App\Http\Controllers;
use App\Models\Activity;
use Illuminate\Http\Request;
class ActivityController extends Controller
{
public function index(Request $request)
{
$activities = Activity::paginate(10)->withQueryString();
return response()->json($activities);
}
}
In page file, we can use the component like this:
<script setup>
import { ref, onUpdated, onMounted } from 'vue'
import PaginationBar from '../components/PaginationBar.vue'
const activities = ref(null);
const load = () => {
// Fetch API here and set it response as activities value.
}
onUpdated(() => load())
onMounted(() => load())
</script>
<template>
<PaginationBar route_name="activities" :links="activities.links" />
</template>
We create a pagination component file (src/components/PaginationBar.vue
).
<script setup>
import { onMounted, ref, watch } from "vue";
import { RouterLink } from 'vue-router'
const props = defineProps({
links: Array,
route_name: String,
});
const buttons = ref([]);
const button_prev_label = ref(null);
const button_next_label = ref(null);
const init = () => {
let links = props.links;
let selected_index = null;
// remove prev & next
links.shift();
links.pop();
// select index
links.map((link, i) => {
if (link.active) selected_index = i;
});
button_prev_label.value = links[selected_index - 1]?.label;
button_next_label.value = links[selected_index + 1]?.label;
buttons.value = links;
}
watch(
() => props.links,
() => init(),
);
onMounted(() => init());
</script>
<template>
<div v-if="buttons.length > 1" class="flex justify-center my-5">
<div class="flex flex-wrap justify-center py-2 min-w-2/5 rounded-lg shadow">
<RouterLink
class="px-4 py-2 ml-2 mr-4 text-sm rounded-lg"
:class="button_prev_label ? 'text-slate-300 focus:text-white-300 hover:bg-slate-800 font-bold' : 'text-slate-500'"
:to="{ name: props.route_name, query: { page: button_prev_label } }"
>
Previous
</RouterLink>
<template v-for="(btn, i) in buttons" v-bind:key="i">
<div
v-if="btn.label == '...'"
class="px-4 py-2 mx-0.5 text-sm font-bold rounded-lg text-slate-500"
>
{{ btn.label }}
</div>
<RouterLink
v-else
class="px-4 py-2 mx-0.5 text-sm font-bold rounded-lg hover:bg-slate-800 focus:text-main-500"
:class="btn.active ? 'bg-slate-800 text-white' : 'text-slate-500'"
:to="{ name: props.route_name, query: { page: btn.label } }"
>
{{ btn.label }}
</RouterLink>
</template>
<RouterLink
class="px-4 py-2 ml-4 mr-2 text-sm rounded-lg"
:class="button_next_label ? 'text-slate-300 focus:text-white-300 hover:bg-slate-800 font-bold' : 'text-slate-500'"
:to="{ name: props.route_name, query: { page: button_next_label } }"
>
Next
</RouterLink>
</div>
</div>
</template>
As you can see, I use props routename
so that I can pass a route name from Vue.js router and not from Laravel router.
This is example I have. Anyone can use and maybe enhance for a better use case.
Thank you.
Top comments (0)