ASP.NET - MVC - Phân trang ASP.NET MVC

Phân trang trong ASP.NET MVC

Phân trang (pagination, paging) là một yêu cầu rất quan trọng khi hiển thị số lượng dữ liệu lớn. Bạn không nên hiển thị hàng trăm hàng ngàn dòng dữ liệu trong bảng cùng lúc. Thay vào đó bạn nên hiển thị, ví dụ, 100 dòng dữ liệu đầu tiên. Một khối 100 dòng dữ liệu này được gọi là một trang (page).

Khi người dùng có nhu cầu sẽ hiển thị 100 dòng tiếp theo. Khối lượng dữ liệu này tạo thành trang thứ 2. V.v.

Người dùng có thể lựa chọn nhảy thẳng tới trang thứ n bất kỳ, nhảy về trang đầu tiên, nhảy về trang cuối cùng, nhảy tới trang kế tiếp của trang hiện tại, nhảy tới trang trước của trang hiện tại.

Để thực hiện phân trang chúng ta cần cài Cài đặt gói PagedList.Mvc 

Ở Visual Studio, bạn chọn mục Tools -> Library Package Manager -> Manage NuGet Packages… như hình sau.


Sau đó gõ PagedList.Mvc bên ô Search góc trái trên cùng, chọn gói PagedList.Mvc để cài đặt, nhấn nút Install.
 

Phân trang


Sau khi cài đặt xong gói phân trang PagedList.Mvc, bạn sẽ sử dụng gói này để phân trang.

Sửa phương thức Index, tập tin Controller/BookController.cs

Bạn mở tập tin Controller/BookController.cs, nhúng thêm thư viện using PagedList; ở đầu trang, sau đó ở phương thức Index sửa như sau.

..........
using PagedList; // thêm thư viện này

namespace BookStoreManager.Controllers
{
    public class BookController : Controller
    {
        private BookStoreEntity db = new BookStoreEntity();

        // GET: Book
        public ActionResult Index(int? page)
        {
            // 1. Tham số int? dùng để thể hiện null và kiểu int
            // page có thể có giá trị là null và kiểu int.

            // 2. Nếu page = null thì đặt lại là 1.
            if (page == null) page = 1;
            // 3. Tạo truy vấn, lưu ý phải sắp xếp theo trường nào đó, ví dụ OrderBy
            // theo BookID mới có thể phân trang.
            var books = db.Books.Include(b => b.Author).Include(b => b.Category).OrderBy(b=>b.BookID);
            // 4. Tạo kích thước trang (pageSize) hay là số Link hiển thị trên 1 trang
            int pageSize = 3;

            // 4.1 Toán tử ?? trong C# mô tả nếu page khác null thì lấy giá trị page, còn
            // nếu page = null thì lấy giá trị 1 cho biến pageNumber.
            int pageNumber = (page ?? 1);

            // 5. Trả về các Link được phân trang theo kích thước và số trang.
            return View(books.ToPagedList(pageNumber, pageSize));
        }
........
}

Trong đoạn mã trên, bạn có thể chú ý chúng ta thêm 1 số tham số page với kiểu int?. Kiểu int? mô tả 1 giá trị có thể là 1 số kiểu nguyên (int) kèm theo giá trị null. Như vậy tham số page có thể là null hoặc bất kỳ giá trị kiểu int nào. Tại sao lại dùng như vậy? Đơn giản là vì đường dẫn http://localhost:xxxx/Book có lúc kèm theo tham số truy vấn page như thế này http://localhost:45033/Book?page=1 hoặc có thể là http://localhost:45033/Book?page=abc hoặc không mô tả gì cả như link đầu tiên. Như vậy cách sử dụng int? rất tinh tế, giúp ép biến page vào 2 kiểu: một là int, hai là null (dành cho trường hợp tham số truy vấn là chuỗi). Điều này loại luôn trường hợp bị hack SLQ Injection trong ASP.NET MVC.

Tiếp theo, nếu page là null, chúng ta đặt mặc định là 1 ở mục 2. Ở mục 3, chúng ta tạo câu truy vấn, lưu bạn phải sort theo 1 trường nào đó bất kỳ (ví dụ là LinkID) vì thư viện phân trang yêu cầu như vậy. Mục 4 mô tả kích thước trang hay số lượng record (Link) hiển thị trên 1 trang.

Mục 5 mô tả số trang hiện tại. Mục này chúng ta thấy sự xuất hiện của toán tử ?? và đây là 1 toán tử tắt của trường hợp điều kiện so sánh null. Toán tử này mô tả nếu vế trái so sánh là null thì lấy giá trị vế phải, ngược lại lấy giá trị vế trái. Để hiểu rõ hơn bạn có thể xét ví dụ sau.

Ví dụ nếu x = null thì y sẽ lấy kết quả vế trái của toán tử ??, tức là 1. Ngược lại, kết quả b là 5 do a khác null.

int? x = null;
int y = x ?? 1; // Kết quả y là 1, do x = null.
 
int? a = 5;
int b = a ?? 1; // Kết quả b là 5 do a không phải là null.

Cuối cùng, mục 5 mô tả kết quả trả về bằng phương thức ToPagedList() với tham số pageNumber và pageSize.

Sửa tập tin Views/Book/Index.cshtml

Bạn mở tập tin này và sửa nội dung như sau. Bạn có thể copy, paste vào code cho nhanh.

<!-- Thêm thư viện -->
@model PagedList.IPagedList<BookStoreManager.Models.Book>
@using PagedList.Mvc;
@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Danh sách sách</h2>

<p>
    @Html.ActionLink("Thêm sách mới", "Create", null, new { @class = "btn btn-warning" })
</p>
<!-- Sửa lại các cộ tiêu đề -->
<table class="table">
    <tr>
        <th style="width:200px">
            Tên sách
        </th>
        <th>
            Chủ đề
        </th>
        <th>
            Giá
        </th>
        <th>
            Hình ảnh
        </th>

        <th>
            Ngày xuất bản
        </th>

        <th>
            Tên tác giả
        </th>

        <th>Sửa|Xem|Xóa</th>
    </tr>

    @foreach (var item in Model)
    {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.Title)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Category.CategoryName)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Price)
        </td>
        <td>
            <img src="~/bookimages/@item.Images" />
        </td>

        <td>
            @Html.DisplayFor(modelItem => item.Published)
        </td>

        <td>
            @Html.DisplayFor(modelItem => item.Author.AuthorName)
        </td>

        <td>
            @Html.ActionLink("Sửa", "Edit", new { id = item.BookID }) |
            @Html.ActionLink("Xem", "Details", new { id = item.BookID }) |
            @Html.ActionLink("Xóa", "Delete", new { id = item.BookID })
        </td>
    </tr>
    }

</table>
<br />
<br />
<!-- Thêm phân trang vào -->
Trang @(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber) / @Model.PageCount

@Html.PagedListPager(Model, page => Url.Action("Index",
    new { page }))
<!-- Kết thúc -->

Ở đoạn mã trên, ghi chú phần đầu chúng ta thay đổi mô hình để sử dụng thư viện phân trang, đó là.

@model PagedList.IPagedList<DammioMVC1.Models.Link>
@using PagedList.Mvc;

Ở đoạn mã trên, ghi chú phần giữa chúng ta thay đổi mô hình để sử dụng thư viện phân trang, đó là.

<!-- Sửa lại các cộ tiêu đề -->
<table class="table">
    <tr>
        <th style="width:200px">
            Tên sách
        </th>
        <th>
            Chủ đề
        </th>
        <th>
            Giá
        </th>
        <th>
            Hình ảnh
        </th>

        <th>
            Ngày xuất bản
        </th>

        <th>
            Tên tác giả
        </th>

        <th>Sửa|Xem|Xóa</th>
    </tr>

Ở phần cuối, chúng ta thêm đoạn mã để hiển thị số trang và số trang hiện tại đó là.

<br />
Trang @(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber) / @Model.PageCount
 
@Html.PagedListPager(Model, page => Url.Action("Index",
    new { page }))

Ở control PagedListPager, chúng ta mô tả mô hình với số trang là liên kết với tham số Controller (Index) và tham số trang.

Chạy ứng dụng ta thấy:

Sau khi sửa mã nguồn như trên, bạn build dự án, chạy link http://localhost:xxxx/Book và bấm thử số trang để xem kết quả.