分頁一直是個有點難度但做法又大同小異的議題,Laravel 本身提供的Paginate除了針對自己的Blade很方便之外,對於前端來說也是滿友善的,如果有特別需求,Laravel也提供了可以自訂的方法;
今天我只想先確認一下原本Laravel提供的分頁方式,是否可以直接給前端使用,至於進階的自訂做法,可能等之後有明確需求時再來進行。
因為 Paginate
是基於 Model
的一個方法,所以要從 Repository
中來修改取出的資料方式:
app\Repositories\SubjectRepository.php
function subjectsInBank($bank_id)
{
return $this->subject->where('bank_id',$bank_id)->get();
}
改成
function subjectsInBank($bank_id)
{
//資料筆數是暫時填的,未來會做成可以參數傳入
return $this->subject->where('bank_id',$bank_id)->paginate(10);
}
透過vue的瀏灠器工具可以看到 Paginate
傳到前端的整包物件內容,資料本身在 data
這個屬性,分頁項目在 links
中,其它則是分頁相關的各種資訊:
修改一下前端原本接收資料的方式:
resources\js\Pages\Backstage\Subjects.vue
.....略
//題目資料改由subjects.data取得
<div v-for="subject in subjects.data"
:key="subject.id"
class="w-full border rounded-xl flex py-2 px-4 bg-green-400 justify-between">
<div>{{ subject.seq }}. {{ subject.subject }}</div>
<div>
<Link :href="route('subject.edit',subject.id)" method="get" as="button">編輯</Link>
/
<Link :href="route('subject.destroy',subject.id)" method="delete" as="button">刪除</Link>
</div>
</div>
.....略
接著在下方增加分頁的資訊:
resources\js\Pages\Backstage\Subjects.vue
.....略
<div v-if="subjects.links.length > 3">
<div class="flex flex-wrap -mb-1">
<template v-for="(page, idx) in subjects.links" :key="idx">
<div v-if="page.url === null"
class="mr-1 mb-1 px-4 py-3 text-sm leading-4 text-gray-400 border rounded"
v-html="page.label" />
<Link v-else
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-blue-700 text-white': page.active }"
:href="page.url"
v-html="page.label" />
</template>
</div>
</div>
這樣就可以快速的完成一個分頁的功能,並且搭配 Inertia
的 Link
組件使用ajax的方式來載入各頁面。
當然,我們不會只有這個頁面使用到這個功能,所以我們可以把這個下方分頁連結做成一個組件,讓其它有需要的地方可以更簡單的使用:
resources\js\QuizComponents\Paginator.vue
<script setup>
import {Link } from "@inertiajs/inertia-vue3";
defineProps({links:Array})
</script>
<template>
<div v-if="links.length > 3" class="my-4">
<div class="flex flex-wrap ml-[1px] -mb-1 justify-center">
<template v-for="(page, idx) in links" :key="idx">
<!--因為分頁的字串中帶有特殊符號,所以透過v-html來轉義-->
<div v-if="page.url === null"
class="mb-1 px-4 py-3 text-sm leading-4 text-gray-400 border rounded"
v-html="page.label" />
<Link v-else
class="-ml-[1px] mb-1 px-4 py-3 text-sm leading-4 border rounded hover:bg-slate-200 focus:border-indigo-500 focus:text-indigo-500"
:class="{'bg-blue-700 text-white hover:bg-blue-500': page.active}"
:href="page.url"
v-html="page.label" />
</template>
</div>
</div>
</template>
在需要分頁的頁面可以簡單的引入並帶入參數:
resources\js\Pages\Backstage\Subjects.vue
.....略
<Paginator :links="subjects.links" />
.....略
如果資料總筆數不多的話,另外一種做法是把資料全部丟到前端,在前端直接做分頁(可以使用datatable之類的套件
),但資料量大的時候,光等資料下載完畢就是一個問題,以我的這個應用來說,最後題目都是以千起算的筆數,所以最好的做法還是由後端來提供適量的分頁資料就好。