Tạo HTML phân trang sử dụng BootStrap trong ASP.NET Core
Khi hiện thị một danh sách dữ liệu dài, có nhu cầu chia nhỏ thành từng trang ngắn - cho thêm vào điều hướng để người dùng lặt trang - chuyển trang.
Ta sẽ xây dựng một Partial hiện thị thanh điều hướng này theo dữ liệu gửi đến. Giả thiết đầu vào phát sinh HTML sẽ gồm các dữ liệu sau:
countPages
- số nguyên - là tổng số trang của dữ liệucurrentPage
- số nguyên - trang hiện tạigenerateUrl
- là một delegate -Func<int p, string url>
nhận thao số p là chỉ mục trang trả về chuỗi là Url của trang. Ví dụp = 5
trả về/product/?page=5
- delegate này phát sinh Url theo Page, Controller ... như thế nào là do bạn xây dựng khi truyền tham số. Xem thêm Action và Func
Nội dung của Partial __Paging.cshtml
sẽ lưu ở Pages/Shared/_Paging.cshtml
@* Model: - currentPage:int - Trang hiện tại - countPages:int - tổng số trang - generateUrl:delegate Func<int?, string> trả về URL tương ứng với trang p (1,2,3 ...) *@ @model dynamic @{ int currentPage = Model.currentPage; int countPages = Model.countPages; var generateUrl = Model.generateUrl; if (currentPage > countPages) currentPage = countPages; if (countPages <= 1) return; int? preview = null; int? next = null; if (currentPage > 1) preview = currentPage - 1; if (currentPage < countPages) next = currentPage + 1; // Các trang hiện thị trong điều hướng List<int> pagesRanges = new List<int>(); int delta = 5; // Số trang mở rộng về mỗi bên trang hiện tại int remain = delta * 2; // Số trang hai bên trang hiện tại pagesRanges.Add(currentPage); // Các trang phát triển về hai bên trang hiện tại for (int i = 1; i <= delta; i++) { if (currentPage + i <= countPages) { pagesRanges.Add(currentPage + i); remain --; } if (currentPage - i >= 1) { pagesRanges.Insert(0, currentPage - i); remain --; } } // Xử lý thêm vào các trang cho đủ remain //(xảy ra ở đầu mút của khoảng trang không đủ trang chèn vào) if (remain > 0) { if (pagesRanges[0] == 1) { for (int i = 1; i <= remain; i++) { if (pagesRanges.Last() + 1 <= countPages) { pagesRanges.Add(pagesRanges.Last() + 1); } } } else { for (int i = 1; i <= remain; i++) { if (pagesRanges.First() - 1 > 1) { pagesRanges.Insert(0, pagesRanges.First() - 1); } } } } } @* PHÁT SINH HTML*@ <ul class="pagination"> <!-- Previous page link --> @if (preview != null) { <li class="page-item"> <a class="page-link" href="@generateUrl(preview)">Trang trước</a> </li> } else { <li class="page-item disabled"> <a class="page-link" href="#" tabindex="-1" aria-disabled="true">Trang trước</a> </li> } <!-- Numbered page links --> @foreach (var pageitem in pagesRanges) { if (pageitem != currentPage) { <li class="page-item"> <a class="page-link" href="@generateUrl(pageitem)"> @pageitem </a> </li> } else { <li class="page-item active" aria-current="page"> <a class="page-link" href="#">@pageitem <span class="sr-only">(current)</span></a> </li> } } <!-- Next page link --> @if (next != null) { <li class="page-item"> <a class="page-link" href="@generateUrl(next)">Trang sau</a> </li> } else { <li class="page-item disabled"> <a class="page-link" href="#" tabindex="-1" aria-disabled="true">Trang sau</a> </li> } </ul>
Bất kỳ khi nào muốn chèn HTML phân trang bạn có thể chèn bằng cách dùng
<partial name="_Paging" model="@datapaging" />
Trong đó currentPage
, currentPage
, generateUrl
Ví dụ chạy thử trong một Razor Page:
@{ // xây dựng Func phát sinh Url từ page ./Index Func<int?,string> generateUrl = (int? _pagenumber) => { return Url.Page("./User", new {pageNumber = _pagenumber}); }; var datapaging = new { currentPage = 2, // trang hiện tại countPages = 20, // tổng số trang generateUrl = generateUrl }; } <partial name="_Paging" model="@datapaging" />
Kết quả như sau:
Khi sử dụng thực tế các tham số countPages, currentPage được truyền đến từ PageModel, Controller thường là kết quả tính toán dựa trên truy vấn số lượng phần tử cần hiện thị từ một bảng dữ liệu
Kỹ thuật truy vấn LINQ lấy số phần tử từng trang
Khi bạn viết một câu truy vấn LINQ ví dụ:
var products = from p in dbproducts select p;
Trong đó dbproducts là nguồn cấp dữ liệu nào đó, ví dụ DbSet trong DbContext ..., thì
products
là một đối tượng kiểu IQueryable
, bạn có thể bỏ qua một số phần
từ đầu chỉ lấy các phần tử sau bằng cách dùng Skip(n)
, bạn
chỉ lấy ra một số phần tử bằng cách dùng Take(n)
Ví dụ, bỏ qua 100 phần tử đầu tiên, tiếp theo chỉ lấy ra 10 phần tử thì bạn viết
var pros = products.Skip(100).Take(10);
Bạn có thể áp dụng kỹ thật này để lấy ra số phần tử theo từng trang
var products = from p in dbproducts select p; // Lấy tổng số dòng dữ liệu var totalItems = products.Count(); // Tính số trang hiện thị (mỗi trang hiện thị ITEMS_PER_PAGE mục do bạn cấu hình = 10, 20 ...) int totalPages = (int)Math.Ceiling((double)totalItems / ITEMS_PER_PAGE); // Lấy phần tử trong hang hiện tại (pageNumber là trang hiện tại - thường Binding từ route) var pros = products.Skip(ITEMS_PER_PAGE * (pageNumber - 1)).Take(ITEMS_PER_PAGE).ToList();
Code trên có thể viết trong Controller, PageModel ... bạn có được trang hiện tại, tổng số trang, các phần tử của trang hiện tại để chuyển cho View - hiện thị phần tử và tạo HTML Paging