Skip to content

Project 11 Create Pagination Search

Theethawat Savastham edited this page Jul 13, 2024 · 4 revisions

ในส่วนนี้จะเป็นส่วนของการสร้าง Pagination และ Search ตั้งแต่ที่ Database เนื่องจากเมื่่อข้อมูลมีมากขึ้น การที่เราจะดึงข้อมูลมาทั้งหมดมาแสดงที่หน้าเว็บจะทำให้หน้าเว็บช้าลง และการที่เราจะทำการค้นหาข้อมูลจากข้อมูลทั้งหมดก็จะทำให้หน้าเว็บช้าลงด้วย ดังนั้นเราจะทำการสร้าง Pagination และ Search ที่ Database ก่อน

การสร้าง Pagination

การสร้าง Pagination ผ่าน Mongoose ธรรมดา

เราจะเริ่มจากการสร้าง Pagination ง่ายๆ ก่อน โดยเราจะใช้ที่ Query ข้อมูลจาก Database โดยใช้ limit และ skip ในการ Query ข้อมูล โดย limit จะบอกว่าเราจะดึงข้อมูลกี่ตัว และ skip จะบอกว่าเราจะข้ามข้อมูลกี่ตัว ดังนั้นเราจะสร้าง API ที่จะ Query ข้อมูลจาก Database โดยใช้ limit และ skip ดังนี้

  1. เข้าไปในไฟล์ product.routes.js ใน routes

  2. ใน route get ของ Product สร้างตัวแปร page และ size ออกมา โดย size คือจำนวนข้อมูลที่จะดึงต่อหน้า และ page คือหน้าที่เราจะดึงข้อมูล

  3. สร้าง skip โดย skip คือจำนวนข้อมูลที่เราจะข้ามไป เพราะว่าในคำสั่ง find ของ mongoose เราไม่สามารถใส่ size ลงไปตรง ๆ ได้ ดังนั้นเราจะต้องใช้ skip ในการข้ามข้อมูลไป โดยเรากำหนดให้ skip = (page - 1) * size

  4. ใช้ size และ skip ในการ Query ข้อมูลจาก Database ดังนี้

router.get('/', async (req, res) => {
  const page = 1
  const size = 5
  const skip = (page - 1) * size

  const result = await Product.find().limit(size).skip(skip)
  res.json({ rows: result })
})
  1. ลองดูผลลัพธ์ที่ Postman หรือ Browser ดูว่าข้อมูลที่ได้มาถูกต้องหรือไม่ (ถ้าจำนวน product มีมากกว่า 5 ตัวจะเห็นผล แต่ถ้าน้อยกว่านั้น จะไม่เห็นผล)

  2. จะสังเกตุว่าข้อมูลจะเรียงลำดับจากที่เข้าไปก่อน และถ้าเราเข้าไปเพิ่มข้อมูลใหม่ ข้อมูลใหม่จะอยู่หลังข้อมูลเดิม ดังนั้นเราจะเรียงข้อมูลจากใหม่สุดก่อน โดยเราจะใช้ sort ในการเรียงข้อมูล ดังนี้

router.get('/', async (req, res) => {
  const page = 1
  const size = 5
  const skip = (page - 1) * size

  const result = await Product.find().sort({ _id: -1 }).limit(size).skip(skip)
  res.json({ rows: result })
})

อย่างไรก็ได้ การ Fix size สำหรับการทำ Pagination อาจไม่เหมาะสมเท่าไหร่นัก เพราะว่าบางที บางหน้าใน frontend เราอาจจะต้องการข้อมูลมาก บางหน้าอาจจะต้องการข้อมูลน้อย ดังนั้น เราจะให้ User ที่ใช้ API สามารถกำหนด limit และ page ได้ด้วย

  1. รับค่า page และ size จาก Query String ด้วย req.query และให้ default ให้ page = 1 และ size = 5 ดังนี้
const page = req?.query?.page || 1
const size = req?.query?.size || 5
  1. ใช้ page และ size ในการ Query ข้อมูลจาก Database โดยเราใส่ในโปรแกรม Postman ที่ Query Params ใส่ key page และ size แล้วใส่ค่าตามต้องการลงไป จะพบว่า URL ข้างบนของเราจะเปลี่ยนไปโดยมี ?page=1&size=5 ตามหลัง URL ของเรา สิ่งนี้แหละเราเรียกว่า Query String

  2. ลองดูผลลัพธ์ที่ Postman ดูว่าข้อมูลที่ได้มาถูกต้องหรือไม่ แล้วลองดูว่าเมื่อเปลี่ยน page หรือ size แล้ว ข้อมูลเปลี่ยนแปลงหรือไม่

image

การสร้าง Pagination ลิงค์มาจาก Frontend

สมมติว่าเดิม เรามีหน้า Product List ของเราที่มีหน้าตาคล้ายๆ ประมาณนี้ image

หรือตามโค้ดตัวอย่าง ตัวอย่างโค้ด

เราจะเห็นว่ามันจะออกมาแค่ 5 ชิ้น เพราะเราตั้ง Pagination Size ค่า Default ไว้ที่ 5 และ ไม่ได้มีการส่งค่าอื่นไปที่ API ในตรงนี้ เราจะให้ Frontend ส่ง Query ไปว่า เราจะใช้จำนวน Size เท่าไหร่

  1. ในหน้า ProductList เพิ่ม State สำหรับ Size ดังนี้
const [size, setSize] = useState(5)
  1. ส่วนที่เรา Get Product เราใส่ Query เพิ่ม size ของเราลงไป โดยใช้เครื่องหมาย ? ตามด้วย key ของเราคือ size แล้วตามด้วย value คือ size ใน state
const getAllProducts = () => {
  axios
    .get(`${process.env.REACT_APP_API_URL}/product?size=${size}`)
    .then((res) => {
      setProducts(res?.data?.rows)
      console.log('Products ', res?.data?.rows)
    })
    .catch((error) => {
      console.error('Error', error?.message)
    })
}
  1. สร้าง Dropdown Select สำหรับการเลือก Size ตัวอย่างเช่น
  • สำหรับคนที่ใช้ mui/joy
import { Select, Option } from '@mui/joy'
<div className="flex justify-end my-2">
  <div>
    <div>จำนวนสินค้าต่อหน้า</div>
    <Select value={size} onChange={(event, newValue) => setSize(newValue)}>
      <Option value={5}>5</Option>
      <Option value={10}>10</Option>
      <Option value={15}>15</Option>
      <Option value={20}>20</Option>
    </Select>
  </div>
</div>
  • สำหรับคนที่ใช้ mui/material
import { Select, MenuItem } from '@mui/material'
<div className="flex justify-end my-2">
  <div>
    <div>จำนวนสินค้าต่อหน้า</div>
    <Select value={size} onChange={(event) => setSize(event.target.value)}>
      <MenuItem value={5}>5</MenuItem>
      <MenuItem value={10}>10</MenuItem>
      <MenuItem value={15}>15</MenuItem>
      <MenuItem value={20}>20</MenuItem>
    </Select>
  </div>
  1. จากนั้นให้การเปลี่ยน Size ของเราไป Trigger UseEffect ใหม่ ด้วยการใส่ size ใน Dependency ของ UseEffect ดังนี้
useEffect(() => {
  getAllProducts()
}, [size])
  1. ลองดูผลลัพธ์ทีได้มา

image

การสร้าง Pagination ผ่าน Mongoose ด้วย Aggregate

General

Project 1 Familiar with React

Project 2 Tailwind CSS, Component and Layouting

Project 3 useState, useEffect, React Hook and API Call

Project 4 Basic Express, API and Database Access

Project 5 Frontend Backend Integration

Project 6 Routing

Project 7 Search with react

Project 8 Pushing the Array

Project 9 More complex data structure

Project 10 Mongo Aggregate Pipeline

Project 11 Create Pagination and Search

Clone this wiki locally