ASP.NET MVC - 使用Ajax進行Table分頁(MVC Paging)

在各式各樣的網站中,Data Grid是很常見的功能,將資料庫裡符合查詢條件的資料逐筆列出,但是當資料筆數太多時,一次Show在同一個頁面上不僅頁面的Scroll Bar會拉到死,在那之前要撈出那多資料整個網頁都Hang住了,為了解決這個問題才有Paging這個技巧。

Paging的原理其實就是在SQL Select語法上下功夫,將整個查詢結果根據PageSize(一頁顯示最多筆數), TotalCount(資料總筆數)進行分割,在靠PageIndex(分頁索引)去取得範圍內的資料。使用Entity Framework可以利用Take, Skip...等方法來實作Paging,在ASP.NET MVC就可以搭配使用Entity Framework。然而,像限量這麼懶的人當然有現成就找現成的來用,限量在NuGet就找到一套MvcPaging的套件,作者已經將Paging實作成一個元件,只要將該丟的東西丟進去就有Paging功能了,接下來限量就來介紹如何使用MvcPaging這套Paging套件。

首先,到NuGet搜尋MvcPaging安裝。



接著,因為我們要用Ajax來進行Paging的動作,所以要額外安裝並加入Jquery ajax的Library。





在Controller裡建立頁面的Action,因為限量要用Ajax來Paging,所以Action要建兩個,ProjectManage為主頁面的Action,一開始會先把第一頁的資料撈出來;ProjectList為Ajax GET呼叫取得該分頁資料的Action,因為要用Ajax改變Data Grid,所以要用PartialView。
PTMController.cs


public class PTMController : Controller
{
    PTMDBEntities _db = new PTMDBEntities();

    // GET: PTM
    public ActionResult Index()
    {
        return View();
    }

    public ActionResult ProjectManage()
    {
        var projs = _db.Project.ToList().ToPagedList( 0, 5 );
        return View(projs);
    }

    public ActionResult ProjectList(int? page)
    {
        var index = page.HasValue ? page.Value - 1 : 0;
        var projs = _db.Project.ToList().ToPagedList( index, 5);
        return PartialView( "_ProjectList", projs );
    }
}

在ProjectList接收到page(分頁號碼)後,先將page轉為index(PageIndex)值,PageIndex與PageNumber的關係差別在於PageIndex起始為0而PageNumber起始為1。接著'使用Entity Framework _db.Project取得所有資料再轉成PagedList並傳入PartialView中。

ProjectManage.cshtml為主頁面,內含有_ProjectList PartialView,在初始接收到第一頁的資料時會透過Html.Partial將第一頁的Model傳入_ProjectList頁面。
ProjectManage.cshtml



@model IEnumerable<WebApplication.Models.Project>
@{
    ViewBag.Title = "ProjectManage";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>ProjectManage</h2>

<div id="proj_list_container">
    @Html.Partial( "_ProjectList", Model )
</div>

_ProjectList.cshtml為Data Grid Paging的PartialView,這裡將Model的資料逐筆列出,這裡要注意的是,因為限量要用讓程式自動去取得該欄位名稱,所以讓這個頁面去繼承IEnumerable<>,這樣才可以使用Html.DisplayNameFor,不然Html.DisplayNameFor是不支援IPagedList<>,但IPagedList<>是繼承IEnumerable<>的,所以限量在後面將Model轉型為IPagedList<>,這樣後續使用才不會有問題。Paging一般分為兩個部分,一為Show資料的Data Grid,另一為Pager,Pager就是切換資料頁的元件,MvcPaging把Pager寫成一個Html擴充元件,直接使用Html.Pager()並丟入Model的PageSize, PageNumber, TotalItemCount就OK了,如果是Ajax的話就要再丟一個AjaxOption,指定欲更新Data Grid的ID與對應的Action即可。

_ProjectList.cshtml



@model IEnumerable<WebApplication.Models.Project>
@{
    var pagedlist = (IPagedList)Model;
}

<table class="table">
    <thead>
        <tr>
            <th>@Html.DisplayNameFor(model => model.Name)</th>
            <th>@Html.DisplayNameFor( model => model.Description )</th>
            <th>@Html.DisplayNameFor( model => model.StartTime )</th>
            <th>@Html.DisplayNameFor( model => model.EndTime )</th>
        </tr>
    </thead>
    <tbody>
        @foreach ( var modelItem in Model )
        {
            <tr>
                <td>@modelItem.Name</td>
                <td>@modelItem.Description</td>
                <td>@modelItem.StartTime</td>
                <td>@modelItem.EndTime</td>
            </tr>
        }
    </tbody>
</table>
<div style="text-align:center">
    @Html.Pager( pagedlist.PageSize, pagedlist.PageNumber, pagedlist.TotalItemCount,
        new AjaxOptions { UpdateTargetId = "proj_list_container" } ).Options(
            o => o.Action( "ProjectList" ).DisplayTemplate( "_BootstrapPagingTemplate" )
                .MaxNrOfPages( 4 ) )
</div>

在_ProjectList.cshtml的Pager裡,限量多加了DisplayTemplate, MaxNrOfPages,這是MvcPaging所提供的貼心設計,MaxNrOfPages設定的數字代表的是Pager從初始往後顯示的Paging Link數量,例如在上面限量設定為四,結果會顯示<< 1 2 3 4 ... 6 >>,因為最後一頁會顯示,所以總和會是1(<<) + 4(1, 2, 3, 4) + 1(...) + 1(6) + 1(>>) = 8。DisplayTemplate是讓開發人員能夠客製化Pager樣式的選項,作者將客製化Pager部分轉換成PartialView,所以要建立一個Pager的PartialView,這裡要注意的是,程式會去尋找DisplayTemplates目錄底下的Pager PartialView,限量認為這PartialView要放在Share底下,所以在Share底下建立一個DisplayTemplates資料夾,將客製化的Pager PartialView放在此。



_BootstrapPagingTemplate.cshtml


@model PaginationModel
<ul class="pagination">
    @foreach ( var link in Model.PaginationLinks )
    {
        @BuildLink( link )
    }
</ul>

@helper BuildLink( PaginationLink link )
{
    var liBuilder = new TagBuilder( "li" );
    if ( link.IsCurrent )
    {
        liBuilder.MergeAttribute( "class", "active" );
    }
    if ( !link.Active )
    {
        liBuilder.MergeAttribute( "class", "disabled" );
    }

    var aBuilder = new TagBuilder( "a" );
    if ( link.Url == null )
    {
        aBuilder.MergeAttribute( "href", "#" );
    }
    else
    {
        aBuilder.MergeAttribute( "href", link.Url );
    }

    // Ajax support
    if ( Model.AjaxOptions != null )
    {
        foreach ( var ajaxOption in Model.AjaxOptions.ToUnobtrusiveHtmlAttributes() )
        {
            aBuilder.MergeAttribute( ajaxOption.Key, ajaxOption.Value.ToString(), true );
        }
    }

    if ( link.DisplayText == "«" )
    {
        aBuilder.InnerHtml = "«";
    }
    else if ( link.DisplayText == "»" )
    {
        aBuilder.InnerHtml = "»";
    }
    else
    {
        aBuilder.SetInnerText( link.DisplayText );
    }
    liBuilder.InnerHtml = aBuilder.ToString();

    @Html.Raw( liBuilder.ToString() )
}

_BootstrapPagingTemplate.cshtml為參考官網的範例,此範例使用TagBuilder依據相關條件產生出Bootstrap樣式的Pager。
執行結果:




這樣就簡單快速完成MVC上的Paging功能,但一直用現成的東西卻不搞懂原理是很難進步的,所以建議使用歸使用,有空還是得要去看看實作的原理。


參考來源:
GitHub - MvcPaging





留言