1 of 25

Kỹ thuật phân trang sử dụng components

Create by: Hữu Định - Minh Đức

2 of 25

Nội dung chính

  1. Mô tả ứng dụng
  2. Thuật toán
  3. Code

3 of 25

1. Mô tả ứng dụng

4 of 25

1. Mô tả ứng dụng

Input: Ta có 1 danh sánh khách hàng lưu trữ trong 1 file json

Process:

  • Hiển thị tất cả dữ liệu khách hàng lên UI cho người dùng nhìn thấy
  • Phân trang mỗi màn hình hiển thị 15 Khách hàng
  • Có các số để chọn trang bất kỳ
  • Chỉ hiển thị 6 số tương ứng với 6 trang gần nhau
  • Có nút next/prev về trước và sau 1 trang
  • Khi click về trang đầu hoặc trang cuối thì ẩn hai nút next/prev
  • Có nút Begin/End về đầu và cuối của danh sách
  • Khi click vào nút Begin thì ẩn nút Begin và Prev
  • Khi click vào nút End thì ẩn nút End và Next

5 of 25

2. Thuật toán

6 of 25

1. Thuật toán

Ý tưởng phân trang:

Chia dữ liệu thành các phần bằng nhau và chỉ hiện thị 1 phần dữ liệu được chia

  • Đưa ra giới hạn mỗi trang có thể show: paginationLimit = 15

  • Tính toán xem có bao nhiêu trang: pageCount = Math.ceil(data.length / paginationLimit);

  • Dữ liệu của 1 trang bất kỳ sẽ nằm trong khoảng

Page hiện tại currRange = pageNum * paginationLimit;

Trước 1 page prevRange = (pageNum - 1) * paginationLimit;

  • Loop dữ liệu thỏa mãn điều kiện trên push vào newArray

data.forEach((item, index) => {

if (index >= prevRange && index < currRange) {

newData.push(item)

}

});

7 of 25

3. Code

8 of 25

3. Code PageSplit

3.1 Cấu trúc thư mục / Tạo file: index.html

Yêu cầu:

  • Cài đặt extension Watch SASS
  • Live Server

Tạo file: index.html

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8">

<meta http-equiv="X-UA-Compatible" content="IE=edge">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>Page Split</title>

<link rel="stylesheet" href="https://huudinh.github.io/assets/sass/lib.min.css">

<link rel="stylesheet" href="./assets/sass/pagesplit.min.css">

</head>

<body>

<div id="root"></div>

<script src="src/index.js" type="module"></script>

</body>

</html>

9 of 25

3. Code PageSplit

3.2 Cấu trúc thư mục / Tạo file: assets/sass/pagesplit.scss

.dataTable{

margin:10px;

&__title{

background:#ededed;

font-weight: 600;

padding:5px 0;

margin:0 0 10px;

border-bottom: 1px solid #ccc;

}

&__content{

margin-right:15px;

margin-left:15px;

.row:nth-child(even){

background:#eee;

}

}

.col{

padding:5px 10px;

}

a{

color:blue;

}

}

.pagination{

display: flex;

justify-content: center;

ul{

padding:0;

}

li{

list-style:none;

cursor: pointer;

box-shadow: 0px 2px 8px rgb(0 0 0 / 10%);

&:nth-child(1){

border-left:1px solid #ccc;

}

}

&__item{

padding:5px 12px;

border-right:1px solid #ccc;

border-bottom:1px solid #ccc;

border-top:1px solid #ccc;

&.active, &:hover{

background:#0d6efd;

color:#fff;

}

}

.d-none{

display: none;

}

}

10 of 25

3. Code PageSplit

3.3 Cấu trúc thư mục / Tạo file: index.js

import Lead from "./components/Lead.js"

const lead = new Lead();

document.getElementById('root').appendChild(lead.render())

11 of 25

3. Code PageSplit

3.4 Cấu trúc thư mục / Tạo file: src/apis/leadList.json

[{

"name": "Nguyễn Văn A",

"phonenumber": "01234567890",

"nameFb": "Đức Đoàn",

"linkFb": "https://www.facebook.com/",

"service": "Nâng mũi",

"branch": "Kangnam Sài Gòn",

"script": "Kịch bản nâng mũi",

"interactive": "https://d1hjkbq40fs2x4.cloudfront.net/2017-12-12/files/eos-6d-mark-ii-sample-image_1723.jpg",

"note": "Không có gì",

"createAt": "2022-12-15"

}, {

"name": "Nguyễn Văn A",

"phonenumber": "01234567891",

"nameFb": "Đức Đoàn",

"linkFb": "https://www.facebook.com/",

"service": "Nâng mũi",

"branch": "Kangnam Hà Nội",

"script": "Kịch bản nâng mũi",

"interactive": "https://d1hjkbq40fs2x4.cloudfront.net/2017-12-12/files/eos-6d-mark-ii-sample-image_1723.jpg",

"note": "Không có gì",

"createAt": "2022-12-16"

}]

12 of 25

3. Code PageSplit

3.5 Cấu trúc thư mục / Tạo file: src/index.js

import Lead from "./components/Lead.js"

const lead = new Lead();

document.getElementById('root').appendChild(lead.render())

13 of 25

3. Code PageSplit

3.6 Cấu trúc thư mục / Tạo file: src/components/Lead.js

import { getLead } from "../apis/lead.js"

import LeadList from "./LeadList.js";

import Pagination from "./Pagination.js";

class Lead {

index = 1

constructor() {

this.$dataTable = document.createElement('div')

this.$dataTable.className = `dataTable`;

this.$table = document.createElement('div')

this.$pagiBox = document.createElement('div')

this.$pagiBox.className = `dataTable__page`

this.getAllLead()

}

setIndex = (index) => {

this.index = index

}

14 of 25

3. Code PageSplit

3.6 Cấu trúc thư mục / Tạo file: src/components/Lead.js

getAllLead = async () => {

try {

const res = await getLead({

pageNum: this.index,

})

this.$leadList = new LeadList({ data: res.render })

this.$pagination = new Pagination({

count: res.pageCount, index: this.index, setIndex: this.setIndex, getAllData: this.getAllLead })

if (res.pageCount === 0) {

this.$table.className = 'text-center';

this.$table.innerHTML = 'Không có dữ liệu';

this.$pagiBox.innerHTML = ''

return

}

this.$table.innerHTML = ''

this.$table.className = 'dataTable__list';

this.$table.appendChild(this.$leadList.render());

this.$pagiBox.innerHTML = ''

this.$pagiBox.appendChild(this.$pagination.render());

} catch (e) {

console.log(e);

return

}

}

15 of 25

3. Code PageSplit

3.6 Cấu trúc thư mục / Tạo file: src/components/Lead.js

render() {

this.$dataTable.appendChild(this.$table);

this.$dataTable.appendChild(this.$pagiBox);

return this.$dataTable;

}

}

export default Lead

16 of 25

3. Code PageSplit

3.7 Cấu trúc thư mục / Tạo file: src/apis/lead.js

const URL = 'src/apis/leadList.json'

export const getLead = async({ pageNum }) => {

try {

const newData = []

// Call API

const response = await fetch(URL);

const data = await response.json();

// Pagination

const paginationLimit = 15

const pageCount = Math.ceil(data.length / paginationLimit);

const prevRange = (pageNum - 1) * paginationLimit;

const currRange = pageNum * paginationLimit;

// getData

data.forEach((item, index) => {

if (index >= prevRange && index < currRange) {

newData.push(item)

}

});

return {

render: newData,

pageCount: pageCount

}

} catch (e) {

console.log(e);

return

}

}

17 of 25

3. Code PageSplit

3.8 Cấu trúc thư mục / Tạo file: src/components/LeadItem.js

class LeadItem {

constructor({ name, phonenumber, nameFb, linkFb, service, branch, script, interactive, note }) {

this.data = {

name: name,

phonenumber: phonenumber,

nameFb: nameFb,

linkFb: linkFb,

service: service,

branch: branch,

script: script,

interactive: interactive,

note: note,

}

this.$tr = document.createElement('div')

this.$tr.className = 'row'

this.$name = new Cell(name);

this.$phonenumber = new Cell(phonenumber);

this.$linkFb = new CellLink(nameFb, linkFb);

this.$service = new Cell(service);

this.$branch = new Cell(branch);

this.$script = new Cell(script);

this.$interactive = new CellLink('Link ảnh', interactive);

this.$note = new Cell(note);

}

18 of 25

3. Code PageSplit

3.8 Cấu trúc thư mục / Tạo file: src/components/LeadItem.js

render() {

this.$tr.appendChild(this.$name.render());

this.$tr.appendChild(this.$phonenumber.render());

this.$tr.appendChild(this.$linkFb.render());

this.$tr.appendChild(this.$service.render());

this.$tr.appendChild(this.$branch.render());

this.$tr.appendChild(this.$script.render());

this.$tr.appendChild(this.$interactive.render());

this.$tr.appendChild(this.$note.render());

return this.$tr

}

}

class Cell {

constructor(name) {

this.$container = document.createElement('div');

this.$container.className = 'col';

this.$container.innerHTML = name;

}

render () {

return this.$container;

}

}

19 of 25

3. Code PageSplit

3.8 Cấu trúc thư mục / Tạo file: src/components/LeadItem.js

class CellLink {

constructor(name, link) {

this.$container = document.createElement('div');

this.$container.className = 'col';

this.$linkText = document.createElement('a');

this.$linkText.target = '_blank';

this.$linkText.href = link;

this.$linkText.innerHTML = name;

}

render () {

this.$container.appendChild(this.$linkText);

return this.$container;

}

}

export { Cell, LeadItem };

20 of 25

3. Code PageSplit

3.9 Cấu trúc thư mục / Tạo file: src/components/LeadList.js

import {LeadItem, Cell} from "./LeadItem.js"

class LeadList {

constructor({ data }) {

this.data = data

this.$container = document.createElement('div')

this.$theadTr = document.createElement('div')

this.$theadTr.className = 'row dataTable__title'

this.$name = new Cell('Họ và tên');

this.$phoneNumber = new Cell('Điện thoại');

this.$linkFb = new Cell('Link Facebook');

this.$service = new Cell('Dịch vụ đăng ký');

this.$branch = new Cell('Chi nhánh');

this.$script = new Cell('Kịch bản');

this.$interactive = new Cell('Tương tác');

this.$note = new Cell('Ghi chú');

this.$tbody = document.createElement('div');

this.$tbody.className = 'dataTable__content';

this.getAllLeads()

}

21 of 25

3. Code PageSplit

3.9 Cấu trúc thư mục / Tạo file: src/components/LeadList.js

getAllLeads = () => {

this.$tbody.innerHTML = ''

for (let i = 0; i < this.data.length; i++) {

this.$item = new LeadItem({

...this.data[i]

})

this.$tbody.appendChild(this.$item.render())

}

return this.$tbody

}

render() {

this.$container.appendChild(this.$theadTr)

this.$container.appendChild(this.$tbody)

this.$theadTr.appendChild(this.$name.render());

this.$theadTr.appendChild(this.$phoneNumber.render());

this.$theadTr.appendChild(this.$linkFb.render());

this.$theadTr.appendChild(this.$service.render());

this.$theadTr.appendChild(this.$branch.render());

this.$theadTr.appendChild(this.$script.render());

this.$theadTr.appendChild(this.$interactive.render());

this.$theadTr.appendChild(this.$note.render());

return this.$container

}

}

export default LeadList

22 of 25

3. Code PageSplit

3.10 Cấu trúc thư mục / Tạo file: src/components/Pagination.js

class Pagination {

constructor({ count, index, setIndex, getAllData }) {

this.index = index //Vị trí hiện tại

this.getAllData = getAllData // hàm lấy tất cả danh sách

this.setIndex = setIndex // hàm set vị trí index

this.dataLength = count // đếm số phần tử của pagination

this.limit = this.dataLength < 6 ? this.dataLength : 6 // giới hạn số phần tử của pagination

this.count = this.limit + this.index - 1 >= this.dataLength ? this.dataLength : this.limit + this.index - 1 // giới hạn phần tử lớn nhất được render của pagination

this.$container = document.createElement('ul')

this.$container.classList.add('pagination')

//nút về đầu

this.$preBtnBegin = document.createElement('li')

this.$preBtnBegin.className = `pagination__item ${this.index <= 1 || this.dataLength === 0 ? 'd-none' : ''}`

this.$preBtnBegin.addEventListener('click', () => {

this.prePageBegin()

})

this.$preBtnBegin.innerHTML = '&laquo;&laquo;'

23 of 25

3. Code PageSplit

3.10 Cấu trúc thư mục / Tạo file: src/components/Pagination.js

//nút Quay lại

this.$preBtn = document.createElement('li')

this.$preBtn.className = `pagination__item ${this.index <= 1 || this.dataLength === 0 ? 'd-none' : ''}`

this.$preBtn.addEventListener('click', () => {

this.prePage()

})

this.$preBtn.innerHTML = '&laquo;'

//nút next

this.$nextBtn = document.createElement('li')

this.$nextBtn.className = `pagination__item ${this.index >= this.dataLength ?'d-none':''}`

this.$nextBtn.addEventListener('click', () => {

this.nextPage()

})

this.$nextBtn.innerHTML = '&raquo;'

//nút next End

this.$nextBtnEnd = document.createElement('li')

this.$nextBtnEnd.className = `pagination__item ${this.index >= this.dataLength ?'d-none':''}`

this.$nextBtnEnd.addEventListener('click', () => {

this.nextPageEnd()

})

this.$nextBtnEnd.innerHTML = '&raquo;&raquo;'

}

24 of 25

3. Code PageSplit

3.10 Cấu trúc thư mục / Tạo file: src/components/Pagination.js

prePageBegin() {

this.index = 1

this.setIndex(this.index)

this.getAllData()

}

prePage() {

this.index = this.index <= 1 ? 1 : this.index - 1

this.setIndex(this.index)

this.getAllData()

}

nextPage() {

this.index = this.index >= this.limit + this.index - 1 ? this.dataLength : this.index + 1

this.setIndex(this.index)

this.getAllData()

}

nextPageEnd() {

this.index = this.limit + 1

this.setIndex(this.index)

this.getAllData()

}

25 of 25

3. Code PageSplit

3.10 Cấu trúc thư mục / Tạo file: src/components/Pagination.js

render() {

this.$container.innerHTML = ''

this.$container.appendChild(this.$preBtnBegin)

this.$container.appendChild(this.$preBtn)

//render số Phần tử

const numTemp = (number) => {

this.$numBtn = document.createElement('li')

this.$numBtn.className = `pagination__item ${this.index === number ? 'active' : ''}`

this.$numBtn.innerHTML = number;

this.$container.appendChild(this.$numBtn)

this.$numBtn.addEventListener('click', () => {

this.index = number

this.setIndex(this.index)

this.getAllData()

})

}

for (let i = this.count === this.dataLength ? this.count - this.limit + 1 : this.index; i <= this.count; i++) {

numTemp(i)

}

this.$container.appendChild(this.$nextBtn)

this.$container.appendChild(this.$nextBtnEnd)

return this.$container

}

}

export default Pagination