สารบัญเนื้อหา
- ลักษณะการทำ Paging ใน ASP.NET Core
- เพิ่ม PagedList Helper Class
- ปรับแต่งตัวช่วยในการทำ Paging (Helper)
- Implement pagination ใน API
- สร้าง Pagination ใน Client App (SPA)
- ใช้งาน ngx-bootstrap pagination module
1. ลักษณะการทำ Paging ใน ASP.NET Core
- เป็นการช่วยเพิ่ม Performance เนื่องจากไม่ต้องโหลดข้อมูลทั้งหมดทีเดียว โดยโหลดข้อมูลทีละหน้าเท่านั้น
- ใช้การส่งผ่านค่าพารามิเตอร์ผ่าน Query String เช่น http://localhost:5000/api/users?pageNumber=1&pageSize=5
- จำนวนต่อหน้า (Page Size) ต้องถูกกำหนดไว้ไม่ให้มากเกินไป
- ควรจะมีผลลัพธ์ปรากฏออกมาทุก ๆ หน้า
- สร้าง Query Command แล้วเก็บไว้ในตัวแปร (Variable)
- ทำการ Query แบบ Async
- สร้างผลลัพธ์จากการ Query และจัดเก็บไว้ใน IQueryable<T>
- มีลักษณะเป็น Singleton queries
2. เพิ่ม PagedList Helper Class
สร้าง PagedList.cs ใน Helpers
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
namespace SocialApp_API.Helpers
{
    public class PagedList<T> : List<T>
    {
        public int CurrentPage { get; set; }
        public int TotalPages { get; set; }
        public int PageSize { get; set; }
        public int TotalCount { get; set; }
        public PagedList(List<T> items, int count, int pageNumber, int pageSize)
        {
            TotalCount = count;
            PageSize = pageSize;
            CurrentPage = pageNumber;
            TotalPages = (int)Math.Ceiling(count / (double)pageSize);
            this.AddRange(items);
        }
        public static async Task<PagedList<T>> CreateAsync(IQueryable<T> source, int pageNumber, int pageSize)
        {
            var count = await source.CountAsync();
            var items = await source.Skip((pageNumber - 1) * pageSize).Take(pageSize).ToListAsync();
            return new PagedList<T>(items, count, pageNumber, pageSize);
        }
    }
}3. ปรับแต่งตัวช่วยในการทำ Paging (Helper)
สร้าง PaginationHeader.cs ใน Helpers
namespace SocialApp_API.Helpers
{
    public class PaginationHeader
    {
        public int CurrentPage { get; set; }
        public int ItemsPerPage { get; set; }
        public int TotalItems { get; set; }
        public int TotalPages { get; set; }
        public PaginationHeader(int currentPage, int itemsPerPage, int totalItems, int totalPages)
        {
            this.CurrentPage = currentPage;
            this.ItemsPerPage = itemsPerPage;
            this.TotalItems = totalItems;
            this.TotalPages = totalPages;
        }
    }
}เพิ่ม AddPagination ใน Extensions.cs
public static void AddPagination(this HttpResponse response, int currentPage, int itemsPerPage, int totalItems, int totalPages)
{
    var paginationHeader = new PaginationHeader(currentPage, itemsPerPage, totalItems, totalPages);
    var camelCaseFormatter = new JsonSerializerSettings();
    camelCaseFormatter.ContractResolver = new CamelCasePropertyNamesContractResolver();
    response.Headers.Add("Pagination", JsonConvert.SerializeObject(paginationHeader, camelCaseFormatter));
    response.Headers.Add("Access-Control-Expose-Headers", "Pagination");
}สร้าง UserParams.cs ใน Helpers
namespace SocialApp_API.Helpers
{
    public class UserParams
    {
        private const int MaxPageSize = 50;
        public int PageNumber { get; set; } = 1;
        private int pageSize = 10;
        public int PageSize
        { 
            get { return pageSize; }
            set { pageSize = (value > MaxPageSize) ? MaxPageSize : value; }
        }
    }
}4. Implement pagination ใน API
ปรับปรุง GetUsers ใน ISocialRepository.cs
Task<PagedList<User>> GetUsers(UserParams userParams);ปรับปรุง GetUsers ใน SocialRepository.cs
public async Task<PagedList<User>> GetUsers(UserParams userParams)
{
    var users = _context.Users.Include(p => p.Photos);
    return await PagedList<User>.CreateAsync(users, userParams.PageNumber, userParams.PageSize);
}ปรับปรุง GetUsers ใน UsersControllers.cs
public async Task<IActionResult> GetUsers([FromQuery]UserParams userParams)
{
    var users = await _repo.GetUsers(userParams);
    var usersToReturn = _mapper.Map<IEnumerable<UserForListDto>>(users);
    Response.AddPagination(users.CurrentPage, users.PageSize, users.TotalCount, users.TotalPages);
    return Ok(usersToReturn);
}ลองรันและทดสอบเรียกไปที่
http://localhost:5000/api/users
http://localhost:5000/api/users?pageNumber=2&pageSize=55. สร้าง Pagination ใน Client App (SPA)
สร้าง Interface pagination.ts ใน _models
export interface Pagination {
    currentPage: number;
    itemsPerPage: number;
    totalItems: number;
    totalPages: number;
}
export class PaginatedResult<T> {
    result: T;
    pagination: Pagination;
}ปรับปรุง getUsers() ใน user.service.ts
getUsers(page?, itemsPerPage?): Observable<PaginatedResult<User[]>> {
  const paginatedResult: PaginatedResult<User[]> = new PaginatedResult<User[]>();
  let params = new HttpParams();
  if (page != null && itemsPerPage != null) {
    params = params.append('pageNumber', page);
    params = params.append('pageSize', itemsPerPage);
  }
  return this.http.get<User[]>(this.baseUrl + 'users', { observe: 'response', params })
    .pipe(
      map(response => {
        paginatedResult.result = response.body;
        if (response.headers.get('Pagination') != null) {
          paginatedResult.pagination = JSON.parse(response.headers.get('pagination'));
        }
        return paginatedResult;
      })
    );
}ปรับปรุง member-list.resolver.ts
import { Injectable } from '@angular/core';
import { Resolve, ActivatedRouteSnapshot, Router } from '@angular/router';
import { User } from '../_models/user';
import { UserService } from '../_services/user.service';
import { AlertifyService } from '../_services/alertify.service';
import { Observable, of } from 'rxjs';
import { catchError } from 'rxjs/operators';
@Injectable()
export class MemberListResolver implements Resolve<User[]> {
    pageNumber = 1;
    pageSize = 5;
    constructor(private userService: UserService,private router: Router, private alertify: AlertifyService) {}
    resolve(route: ActivatedRouteSnapshot): Observable<User[]> {
        return this.userService.getUsers(this.pageNumber, this.pageSize).pipe(
            catchError(error => {
                this.alertify.error('Problem retrieving data');
                this.router.navigate(['/home']);
                return of(null);
            })
        );
    }
}
ปรับปรุง member-list.component.ts
import { Component, OnInit } from '@angular/core';
import { User } from '../../_models/user';
import { UserService } from '../../_services/user.service';
import { AlertifyService } from '../../_services/alertify.service';
import { ActivatedRoute } from '@angular/router';
@Component({
  selector: 'app-member-list',
  templateUrl: './member-list.component.html',
  styleUrls: ['./member-list.component.css']
})
export class MemberListComponent implements OnInit {
  users: User[];
  constructor(private userService: UserService, private alertify: AlertifyService, private route: ActivatedRoute) { }
  ngOnInit() {
    this.route.data.subscribe(data => {
      this.users = data.users.result;
    });
  }
  // loadUsers() {
  //   this.userService.getUsers().subscribe((users: User[]) => {
  //     this.users = users;
  //   }, error => {
  //     this.alertify.error(error);
  //   });
  // }
}
ลองรัน http://localhost:4200 (อย่าลืม logout และ login ใหม่)

จะพบว่าข้อมูลสมาชิกมา 5 รายการแรก (หน้าที่ 1) ตามที่กำหนดแล้ว
6. ใช้งาน ngx-bootstrap pagination module
เราจะใช้ ngx bootstrap ในการทำ paging รายละเอียดเพิ่มเติม https://valor-software.com/ngx-bootstrap/#/pagination
ทำการ imports ใน app.module.ts
PaginationModule.forRoot()import { PaginationModule } from 'ngx-bootstrap/pagination';ปรับปรุง member-list.component.ts
import { Component, OnInit } from '@angular/core';
import { User } from '../../_models/user';
import { UserService } from '../../_services/user.service';
import { AlertifyService } from '../../_services/alertify.service';
import { ActivatedRoute } from '@angular/router';
import { Pagination, PaginatedResult } from 'src/app/_models/pagination';
@Component({
  selector: 'app-member-list',
  templateUrl: './member-list.component.html',
  styleUrls: ['./member-list.component.css']
})
export class MemberListComponent implements OnInit {
  users: User[];
  pagination: Pagination;
  constructor(private userService: UserService, private alertify: AlertifyService, private route: ActivatedRoute) { }
  ngOnInit() {
    this.route.data.subscribe(data => {
      this.users = data.users.result;
      this.pagination = data.users.pagination;
    });
  }
  pageChanged(event: any): void {
    this.pagination.currentPage = event.page;
    this.loadUsers();
  }
  loadUsers() {
    this.userService
      .getUsers(this.pagination.currentPage, this.pagination.itemsPerPage)
      .subscribe((res: PaginatedResult<User[]>) => {
      this.users = res.result;
      this.pagination = res.pagination;
    }, error => {
      this.alertify.error(error);
    });
  }
}ปรับปรุง member-list.component.html
<div class="container mt-5">
  <div class="row">
    <div *ngFor="let user of users" class="col-lg-2 col-md-3 col-sm-6">
      <app-member-card [user]="user"></app-member-card>
    </div>
  </div>
</div>
<div class="d-flex justify-content-center">
  <pagination [boundaryLinks]="true"
              [totalItems]="pagination.totalItems"
              [itemsPerPage]="pagination.itemsPerPage"
              [(ngModel)]="pagination.currentPage"
              (pageChanged)="pageChanged($event)"
            previousText="‹" nextText="›" firstText="«" lastText="»">
  </pagination>
</div>ทดสอบเปลี่ยนหน้าดู






 
                                                                 
                                                                







