avatar

目錄
Node.js 實作分頁

Node.js 實作分頁

一樣延續 使用 Node.js 與 RESTful API 架構來串接 mongoDB 並部署到 Heroku,跟 Node.js 實作登入及驗證 來延伸實作分頁功能。

製作分頁功能,首先要考慮四個要素:

  • 總頁數 = 總資料量/每頁每頁資料量
  • 目前頁數 = 預設第一頁(之後依照 query 參數傳入)
  • 每頁資料量 = 自行設定
  • 當前頁面資料 = 按照目前頁數及每頁資料量找出範圍區間後顯示

後端

有了四個要素,就可以做出功能了。

  1. 連接資料庫後,取得資料
  2. 根據上面的四要素,算出參數
  3. 篩選出要顯示的資料
  4. 回傳資料及參數給 client 端

邏輯控制

在邏輯控制加入分頁方法

./controllers/restaurants.js

Code
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
getRestaurantsPage: (req, res) => {
// 接收前端所查詢的頁面資料 ?page=1
// 由於接收到的資料是字串,用 parseInt() 轉換成數字
let currentPage = parseInt(req.query.page) || 1
db.collection('restaurants').find().toArray((err, result) => {
if (err) return console.log(err)
const totalResult = result.length
// 每頁資料量
const perpage = 3
// 總頁數
const pageTotal = Math.ceil(totalResult / perpage)
// 預設當前頁數,
// let currentPage = 1
// 當前頁不能超過總頁數
if (currentPage > pageTotal) {
currentPage = pageTotal
}
// 第幾筆資料索引範圍
const minItem = (currentPage * perpage) - perpage + 1
const maxItem = (currentPage * perpage)

// console.log(totalResult, pageTotal, currentPage, minItem, maxItem)
const temp = []
result.forEach((item, i) => {
let itemNum = i + 1
if (itemNum >= minItem && itemNum <= maxItem) {
// console.log(item.name, i)
temp.push(item)
}
})
// console.log(temp)
res.status(200).send({
data: temp,
pagination: {
total_pages: pageTotal,
current_page: currentPage,
has_pre: currentPage > 1,
has_next: currentPage < pageTotal,
category: null
}
})
})
},

路由設定

./routes/restaurants.js

router.get('/page', getRestaurantsPage)

API: http://localhost:3000/restaurants/page?page=1

前端

Vue 前端製作部分:

html(使用樣板)

Code
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
<nav aria-label="Page navigation example">
<ul class="pagination">
<li class="page-item" :class="{'disable': !pagination.has_pre}">
<a
class="page-link"
href="#"
aria-label="Previous"
@click.prevent="getLocation(pagination.current_page - 1)"
>
<span aria-hidden="true">&laquo;</span>
<span class="sr-only">Previous</span>
</a>
</li>
<li
class="page-item"
v-for="page in pagination.total_pages"
:key="page"
:class="{'active': pagination.current_page === page}"
>
<a class="page-link" href="#" @click.prevent="getLocation(page)">{{ page }}</a>
</li>
<li class="page-item">
<a
class="page-link"
href="#"
aria-label="Next"
@click.prevent="getLocation(pagination.current_page + 1)"
>
<span aria-hidden="true">&raquo;</span>
<span class="sr-only">Next</span>
</a>
</li>
</ul>
</nav>

加上 pagination 資料欄位

Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
export default {
data: () => ({
...
pagination: {},
...
}),
methods: {
getLocation(page = 1) {
// 用 axios 非同步取得資料
const api = `${process.env.VUE_APP_APIPATH}/restaurants/page?page=${page}`
this.$http.get(api).then(Response => {
this.data = Response.data.data
this.pagination = Response.data.pagination
})
},
},

參考:
六角學院 Node.js 課程
https://wiki.jikexueyuan.com/project/express-mongodb-setup-blog/