Vue 出一個女友吧!-製作分頁邏輯-1

前言

接下來講講製作分頁邏輯的部分,好在本身 API 就已經有判斷分頁了

分頁邏輯

一般來講我們要製作分頁的時候動作是滿多是在的分頁的邏輯上面,首先會有這幾個重點

  • 總共有幾頁 - total_pages
  • 當前在第幾頁 - current_pages
  • 每一頁呈現多少資料 - per_page
  • 頁面頁數

以這個範例 API 來講可以有兩種做法,一種是所有資料的 api,另一種則是使用後端幫我們處理好的 api,但是在製作之前我們會先建立一個 Pagination_1.vue 與 Pagination_2.vue 的檔案,這兩支分別會有兩種做法

  • Pagination_1.vue - 比較原生較重邏輯的方式製作,所以會使用取得全部資料來製作
  • Pagination_2.vue - 使用後端處理好的現成分頁邏輯製作

所以這章節將會拆成兩部分,一部分是講解自己處理資料,另一部分是由後端處理好的方式

模板

這邊會直接使用 bootstrap 所提供的 Pagination 模板做開發 (已轉 pug)

1
2
3
4
5
6
7
8
9
10
11
12
nav(aria-label='Page navigation example')
ul.pagination
li.page-item
a.page-link(href='#') Previous
li.page-item
a.page-link(href='#') 1
li.page-item
a.page-link(href='#') 2
li.page-item
a.page-link(href='#') 3
li.page-item
a.page-link(href='#') Next

分頁邏輯

Pagination_1.vue (所有資料 API)

這邊我們將會使用取得所有資料的 API 來製作

取得所有資料

AJAX 的方法我就直接提供在下方,主要是修改再 Products.vue,所以原本的 getProduct 就會有點不太一樣

getProduct

將會調整成改取得所有資料的 API,那這與原本的 API 是有差異的,原本的 API 拉回來的資料是像這樣子

陣列型態

但取得所有資料的 API 就變成了物件型態,是不太一樣的

物件型態

首先要將原本儲存資料的 products 改成陣列

改成陣列

然後將拉回資料的部分改成 push 的方式

push

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const vm = this;
const url = `${process.env.VUE_APP_HEXURL}/api/${process.env.VUE_APP_HEXNAME}/admin/products/all`;
vm.isLoading = true;
vm.$http.get(url)
.then((response) => {
if (response.data.success) {
vm.products.push(response.data.products);
}
vm.isLoading = false;
})
.catch((error) => {
vm.$bus.$emit('message:push', error, 'danger');
vm.isLoading = false;
});

但是你會發現不能正常顯示資料,原因是出在第一層跑出迴圈是拉出所有陣列的第一層導致,所以正確程式碼部分要多做迴圈處理,但是這邊要注意資料是 object 型態,所以要使用 for...in... 來處理物件資料

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const vm = this;
const url = `${process.env.VUE_APP_HEXURL}/api/${process.env.VUE_APP_HEXNAME}/admin/products/all`;
vm.isLoading = true;
vm.$http.get(url)
.then((response) => {
if (response.data.success) {
let objectProducts = response.data.products;
for(let item in objectProducts) {
vm.products.push(objectProducts[item]);
}
}
vm.isLoading = false;
})
.catch((error) => {
vm.$bus.$emit('message:push', error, 'danger');
vm.isLoading = false;
});

那麼這邊就將資料處理方面給完成了

分頁邏輯

這邊分頁的邏輯是比較長的,也比較複雜唷

首先我們要先取得設置相關的資料欄位(該這樣講嗎?),所以 data 中會新增 pagination 的物件資料

pagination

在前面有講到我們會需要這幾個資料

  • 總共有幾頁 - total_pages
  • 當前在第幾頁 - current_pages
  • 每一頁呈現多少資料 - per_page
  • 頁面頁數

但是這邊還少一個 全部資料數量,所以程式碼就可以這樣寫

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
const vm = this;
const url = `${process.env.VUE_APP_HEXURL}/api/${process.env.VUE_APP_HEXNAME}/admin/products/all`;
vm.isLoading = true;
vm.$http.get(url)
.then((response) => {
if (response.data.success) {
let objectProducts = response.data.products;
for(let item in objectProducts) {
vm.products.push(objectProducts[item]);
}
// 迴圈結束後才開始來取得資料長度
vm.pagination.totalResult = vm.products.length;
// 設置每一頁資料數量
vm.pagination.per_page = 10;
// 統計總頁數
// 公式:總長度 / 每一頁數量,另外這裡會出現小數點,所以要使用 Math.ceil 來無條件進位
vm.pagination.pageTotal = Math.ceil(vm.pagination.totalResult / vm.pagination.per_page);
// 接下來定義當前頁數
// 通常是 1
vm.pagination.currentPage = 1;
// 但是要注意,當前頁數是不可能超過總頁數,所以需要做一個判斷來避免
if (vm.pagination.currentPage > vm.pagination.pageTotal) {
vm.pagination.currentPage = vm.pagination.pageTotal;
}
// 接下來當我們目前位於第二頁時,資料會是 11~20
// 所以這邊會有一段公式來做計算
// 假設當前位於第二頁,所以就是 (10 * 2) - 10 = 10,最後 + 1,所以最小頁數就是 11 開始
// 如果不清楚的時候可以使用結果反推
const minPage = (vm.pagination.currentPage * vm.pagination.per_page) - vm.pagination.per_page + 1; // 11
const maxPage = (vm.pagination.currentPage * vm.pagination.per_page); // 20
console.log(`總資料數量:${vm.pagination.totalResult},每頁數量:${vm.pagination.per_page},總頁數:${vm.pagination.pageTotal},當前頁數:${vm.pagination.currentPage},每頁第一筆:${minPage},每頁最後一筆${maxPage}`)
}
vm.isLoading = false;
})
.catch((error) => {
vm.$bus.$emit('message:push', error, 'danger');
vm.isLoading = false;
});

那麼結果就會像這樣

結果

接下來就準備來做資料處理哩

1
2
3
4
5
6
7
8
9
10
11
12
// 開始做資料處理
// 首先要將所有資料拉出來
vm.products = [];
vm.cacheProducts.forEach((item, index) => {
// 由於 index 是從 0 開始,所以要建立一個變數儲存正確的數量
let num = index + 1;
// 接下來寫上判斷式
// 當 num 比 minPage 大並且比 maxPage 小的時候,就 push 資料進去
if (num >= minPage && num <= maxPage) {
vm.products.push(item);
}
});

這樣子就成功地將資料做了分頁篩選

分頁篩選

但是這邊還有幾個地方還沒有做調整,首先我們必須透過 components 來切換分頁,所以會有一個 Pagination_1.vue 接收我們的當前分頁,另外 getProduct() 也必須額外傳入參數,也就是當前分頁參數,所以這邊的完整程式碼就會變成這樣 (請小心服用很長)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
getProduct(currentPage = 1) { // ES6 預設值
const vm = this;
const url = `${process.env.VUE_APP_HEXURL}/api/${process.env.VUE_APP_HEXNAME}/admin/products/all`;
vm.isLoading = true;
vm.$http.get(url)
.then((response) => {
if (response.data.success) {
const objectProducts = response.data.products;
vm.cacheProducts = [];
for (const item in objectProducts) {
vm.cacheProducts.push(objectProducts[item]);
}
// 迴圈結束後才開始來取得資料長度
vm.pagination.totalResult = vm.cacheProducts.length;
// 設置每一頁資料數量
vm.pagination.per_page = 10;
// 統計總頁數
// 公式:總長度 / 每一頁數量,另外這裡會出現小數點,所以要使用 Math.ceil 來無條件進位
vm.pagination.pageTotal = Math.ceil(vm.pagination.totalResult / vm.pagination.per_page);
// 接下來定義當前頁數
// 通常會是 1
vm.pagination.currentPage = currentPage;
// 但是要注意,當前頁數是不可能超過總頁數,所以需要做一個判斷來避免
if (vm.pagination.currentPage > vm.pagination.pageTotal) {
vm.pagination.currentPage = vm.pagination.pageTotal;
}
// 接下來當我們目前位於第二頁時,資料會是 11~20
// 所以這邊會有一段公式來做計算
// 假設當前位於第二頁,所以就是 (10 * 2) - 10 = 10,最後 + 1,所以最小頁數就是 11 開始
// 如果不清楚的時候可以使用結果反推
const minPage = (vm.pagination.currentPage * vm.pagination.per_page) - vm.pagination.per_page + 1; // 11
const maxPage = (vm.pagination.currentPage * vm.pagination.per_page); // 20
// 開始做資料處理
// 首先要將所有資料拉出來
vm.products = [];
vm.cacheProducts.forEach((item, index) => {
// 由於 index 是從 0 開始,所以要建立一個變數儲存正確的數量
const num = index + 1;
// 接下來寫上判斷式
// 當 num 比 minPage 大並且比 maxPage 小的時候,就 push 資料進去
if (num >= minPage && num <= maxPage) {
vm.products.push(item);
}
});
}
vm.isLoading = false;
})
.catch((error) => {
vm.$bus.$emit('message:push', error, 'danger');
vm.isLoading = false;
});
},

分頁元件

接下來準備製作分頁元件,這邊會使用到的觀念式 props、emit,這邊直接提供模板 template

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<template lang="pug">
div
nav(aria-label='Page navigation example')
ul.pagination.justify-content-center
li.page-item(v-for='pages in paginationService.pageTotal', :key='pages', :class="{'active': paginationService.currentPage === pages}")
a.page-link(href='#', @click.prevent='getPagesService(pages)') {{ pages }}
</template>

<script>
export default {
props: {
paginationService: {
type: Object,
},
},
methods: {
getPagesService(item) {
this.$emit('pageService', item);
},
},
};
</script>

然後回到 products.vue,將 Pagination_1.vue 引入

1
import pagination1Components from '../shared/Pagination_1.vue';

(請記得插入模板內)

那模板那邊插入就是這樣

1
pagination1Components(:paginationService='pagination')

Image

接下來就是切頁做調整,當我們點擊第二頁時,Pagination_1 要將資料往外傳給 getProduct 來做切頁動作,所以就會這樣寫

1
pagination1Components(:paginationService='pagination', v-on:pageService="getProduct")

那麼這樣子就成功做出分頁功能哩~

切頁

並且也可以切換頁面

切頁

補充

如果對於 props 與 emit 不熟悉的話,可以看看我所撰寫的這一篇文章

props 與 emit 的傳遞方式

0%