MVC Paging

Paging là từ mà chúng ta không thể không nhắc tới mỗi khi phát triển một ứng dụng website. Vậy khi phát triển ứng dụng web bằng MVC thì chúng ta sẽ paging bằng cách nào, hôm nay mình xin chia sẻ 1 trong số những cách để thực hiện paging trên size

trong bài viết này mình sẽ thực hiện paging từ BASIC html thuần cho tới việc paging có connect với DB.

Đầu tiên chúng ta hiểu quy trình paging sẽ thực hiện như thế nào

  • Sơ đồ luồng dữ liệu sẽ đi khi thực hiện paging

PagingMVC.jpg

OK mình sẽ bắt đầu thực hiện công việc nào mình sẽ demo với database gồm 2 table là Orders và Customers có diagram như bên dưới

Diagram.jpg

phần View trong demo này sẽ có yêu cầu như sau:

  • khi vào page thì default page = 1
  • load page số 1 với page size đc quy định trong web config
  • Khi click vào page nào thì sẽ list data theo page đó

ở đây mình có data là 100 row, mỗi page là 10 row

database.jpg

File HTML để hiển thị

@{
    ViewBag.Title = "Tutorial MVC Paging";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<script src="~/Scripts/OrderApp/OrderAppJS.js"></script>
@model List<Business.ViewModel.OrdersViewModel>
<div class="container">
        <div class="row" style="text-align:center">
            <h1>Orders Management</h1>
        </div>

        <div class="row">
            <div class="col-lg-12">
                <table class="table" id="tbl-order">

                    <thead>
                        <tr>
                            <th>
                                ID
                            </th>
                            <th>
                                Order Date
                            </th>
                            <th>
                                Status
                            </th>
                            <th>
                                Customer
                            </th>
                            <th>
                                Country
                            </th>
                            <th>
                                Total
                            </th>
                        </tr>
                    </thead>
                    <tbody>
                        @for (int i = 0; i < Model.Count; i++)
                        {
                            <tr>
                                <td>@Model[i].Id</td>
                                <td>@Model[i].OrderDate.ToString("MM/dd/yyyy")</td>
                                <td>@Model[i].Status</td>
                                <td> @Model[i].CustomerName</td>
                                <td>@Model[i].CountryName</td>
                                <td>[email protected][i].TotalPay</td>
                            </tr>
                        }
                    </tbody>
                    <tfoot>
                        <tr>
                            <td colspan="6" style="text-align:right">
                                @for (int i = 1; i <= (int)@ViewBag.TotalPage; i++)
                                {
                                    <a href="#" onclick="OrderAppJs.Paging(@i)">@i</a>
                                }
                            </td>
                        </tr>
                    </tfoot>
                </table>
            </div>
        </div>
</div>

Config Page Size

<configuration>
 <appSettings>
    <add key="DefaultPageSize" value="10" />
  </appSettings>
</configuration>

đây là controller cho lần vào page đầu tiên mục tiêu trả về là:

  • 10 row đầu tiên
  • tổng số page
  public ActionResult PagingSite()
        {
            int totalRow = 0;
            int totalPage = 0;
            var pageSize = int.Parse(WebConfigurationManager.AppSettings("DefaultPageSize"))
            var listOrder = OrderAppBusiness.GetListOrder(1,pageSize, out totalRow);
            if (totalRow > pageSize)
            {
                totalPage = (int)Math.Ceiling((decimal)(totalRow / pageSize));

                if ((totalRow % pageSize) > 0)
                {
                    totalPage += 1;
                }
            }
            else
            {
                totalPage = 1;
            }

            @ViewBag.TotalPage = totalPage;

            return View(listOrder);
        }

paging.jpg

ok chúng ta đã load đc form cho lần đầu tiên, giờ chúng ta sẽ chuyển qua bước thứ 2 là paging

  • mục tiêu của chúng ta là:

    • click page nào thì sẽ load data cho page đó
  • thế mạnh của MVC là partial view kết hợp với Ajax để load data ở những vùng cần thiết mà ko cần reload lại toàn bộ page vì thế nó rất nhanh.

  • tùy vào những yêu cầu đó mà chúng ta có thể sử dụng linh hoạt object trả về của controller (Json, partialView, ...). Ở đây mình đơn thuần chỉ trả về các row của table rồi append vào table đã có sẵn nên controller sẽ trả về là 1 parrtial view

var OrderAppJs = {
  Paging: function (page) {

        $.ajax({
            data: { page: page },
            dataType: "html",
            url: "/Home/PagingSite",
            success: function (data) {
                $("#tbl-order tbody").empty();
                $("#tbl-order tbody").append(data);
            }
        });
    }
}
  public ActionResult PagingSite(int page)
  {
    int totalRow = 0;
    var pageSize = int.Parse(WebConfigurationManager.AppSettings("DefaultPageSize"))
    var listOrder = OrderAppBusiness.GetListOrder(page,pageSize, out totalRow);

    return PartialView(listOrder);
  }
@model List<Business.ViewModel.OrdersViewModel>
@for (int i = 0; i < Model.Count; i++)
{
    <tr>
        <td>@Model[i].Id</td>
        <td>@Model[i].OrderDate.ToString("MM/dd/yyyy")</td>
        <td>@Model[i].Status</td>
        <td>@Model[i].CustomerName</td>
        <td>@Model[i].CountryName</td>
        <td>[email protected][i].TotalPay</td>
    </tr>
}

Follow đoạn code trên như sau

  • user click vào panel paging ở table footer
  • Ajax truyền request lên sever, ở đây là page cần lấy data
  • Controller nhận request, gọi code behavior để xử lý lấy data cho page request, sau khi nhận đc data thì controller đi render Partial view và trả về cho Ajax 1 chuỗi strinhg chính là code html dể append vào table
  • Ajax sau khi complete phiên làm việc thì sẽ nhận đc 1 chuỗi string các row và tiến hành clear body hiện tại và append các row mới vào body table.

Như mình nói lúc nãy Controller ngoài trả về View, PartialView thì mình có thể trả về Json để tiện xử lý 1 số business. Vậy làm sao mình có thể kết hợp vừa trả về view và vừa trả về 1 số object khác. Mình cũng xin chia sẽ thể 1 cách follow như thế này

  • View hoặc PartialView sẽ được render thành chuỗi string và gán vào Json
  • Controller trả về là Json và Ajax sẽ lấy html code từ Json trả về

Các bạn có thể follow theo code bên dưới code Controller

 public ActionResult PagingSite(int page)
  {
    int totalRow = 0;
    var pageSize = int.Parse(WebConfigurationManager.AppSettings("DefaultPageSize"))
    var listOrder = OrderAppBusiness.GetListOrder(page,pageSize, out totalRow);

      return Json(new { Grid = RenderRazorViewToString("PagingSite", listOrder) }, JsonRequestBehavior.AllowGet);
  }

public string RenderRazorViewToString(string viewName, object model)
{
   ViewData.Model = model;
   using (var sw = new StringWriter())
   {
     var viewResult = ViewEngines.Engines.FindPartialView(ControllerContext,
                                                                         viewName);
     var viewContext = new ViewContext(ControllerContext, viewResult.View,
                                             ViewData, TempData, sw);
     viewResult.View.Render(viewContext, sw);
     viewResult.ViewEngine.ReleaseView(ControllerContext, viewResult.View);

     return sw.GetStringBuilder().ToString();
   }
}
Paging: function (page) {
        $.ajax({
            data: { page: page },
            url: "/Home/PagingSite",
            success: function (data) {
                $("#tbl-order tbody").empty();
                $("#tbl-order tbody").append(data.Grid);
            }
        });
    }
}

Còn về code Behavior thì Mình sẽ sử dụng EF (Entity Framework) để thực hiện paging lấy data

  1. đầu tiên mình thực hiện setup cho EF
namespace Business.UnitOfWork
{
    public class OrderAppUnitOfWork : BaseUnitOfWork
    {
        public OrderAppUnitOfWork(OrderAppEntities entity)
            : base(entity)
        {
        }

        public IOrderRepository OrderRepository
        {
            get
            {
                return new OrderRepository(this);
            }
        }

        public ICustomerRepository CustomerRepository
        {
            get
            {
                return new CustomerRepository(this);
            }
        }
    }
}


namespace Business.Repository
{
    public interface IProductRepository : IBaseRepository<Product>
    {
    }
}
namespace Business.Repository
{
    public interface IBaseRepository<T> where T : class
    {
        T Save(T model);

        void Save(IEnumerable<T> models);

        void Delete(T model);

        void Delete(int id);

        void Delete(IEnumerable<T> models);

        T GetObject(int Id);

        IQueryable<T> GetAll();
    }
}

Chi tiết về setup Responsitory mình sẽ update vào bài sau nhé ^^

giờ chúng ta đã setup xong EF, công việc tiếp theo là đi thực hiện paging để lấy dữ liệu.

Để thực hiện việc vận chuyển data mình đã define ra các model sau

    public class OrdersViewModel
    {
        public int Id { get; set; }

        public DateTime OrderDate { get; set; }

        public string Status { get; set; }

        public string CustomerName { get; set; }

        public string CountryName { get; set; }

        public decimal TotalPay { get; set; }

        public int TotalQuantity { get; set; }
    }

để thực hiện công việc này mình tạo 1 layer nằm giữa Controller và Entity FrameWork, mục đích chính của layer này là sẽ xử lý các business, gọi EF xử lý data và trả về cho controller data cần thiết.


namespace Business.Business
{

    public class OrderAppBusiness : BaseBusiness
    {
        /// <summary>
        /// currentPage => nextpage = currentPage + 1
        /// pageSize => row display per page
        /// totalRow => total data row in database
        /// </summary>
        public List<OrdersViewModel> GetListOrder(int currentPage,int pageSize, out int totalRow)
        {
            var listOder = this.OrderAppUnitOfWork.OrderRepository.GetAll().Include(x => x.Customer).Include(x => x.OrderDetails);

            List<OrdersViewModel> listResult = new List<OrdersViewModel>();

            //count total data row in database
            totalRow = listOder.Count();

            //Paging => get data from currentPage - 1 * pageSize to (currentPage * pageSize) + pageSize
            listOder = listOder.Skip((currentPage - 1) * pageSize)
                    .Take(pageSize);

            //Mapping to model
            foreach (var item in listOder.ToList())
            {
                OrdersViewModel order = new OrdersViewModel()
                {
                    Id = item.Id,
                    CustomerName = item.Customer.Name,
                    CountryName = item.Customer.Country,
                    OrderDate = item.OrderDate.Value,
                    Status = item.Status,
                    TotalPay = item.OrderDetails.Select(y => y.Quantity * y.UnitPrice).Sum()
                };

                listResult.Add(order);
            }

            return listResult;
        }
    }
}

Vậy là xong 😄


All Rights Reserved