2012年7月5日 星期四

ASP.NET MVC 資料分頁 MVCPaging 2.0 應用 Part.1:一般、表單(Form)


之前有介紹過 MVCpaging 2.0 的內容「ASP.NET MVC 3 資料分頁 - MvcPaging 2.0.0 Release」,但是那篇文章的內容是著重在 MVCPaging 2.0.0 以之前版本的比較與改變,並沒有說明要如何應用,所以這一篇就要來實際的使用 MVCpaging 來做練習與應用,這一系列的文章將會跟大家來說明如何將一個含有表單的基本資料列表使用 MVCPaging 加上分頁功能,並繼而修改分頁的資料後送伺服端的方式,以及如何修改為 AJAX,這一篇就先從基本的開始吧!

 


我將之前的「Dynamic LINQ + Entity Framework - Part.3:ASP.NET MVC 應用」所做的網站專案拿來做練習,

一開始的基本頁面是如下圖所示,

image

以下是後端 Controller 的 Action 內容,

image

我們就是要把這個網頁的內容使用 MVCPaging 2.0 加上分頁的功能。

 

透過 NuGet 加入 MvcPaging 2.0

image

因為透過 NuGet 所加入的 MvcPaging 2.0 只會加入 Assembly,並不會加入相關的樣式內容,所以這邊提供 MvcPaging 範例檔案所使用的樣式表內容,各位再將 style 內容加入到自己專案的樣式表檔案裡,

/* Pager */
.pager
{
    margin: 8px 3px;
    padding: 3px;
}
.pager .disabled
{
    border: 1px solid #ddd;
    color: #999;
    margin-top: 4px;
    padding: 3px;
    text-align: center;
}
.pager .current
{
    background-color: #6ea9bf;
    border: 1px solid #6e99aa;
    color: #fff;
    font-weight: bold;
    margin-top: 4px;
    padding: 3px 5px;
    text-align: center;
}
.pager span, .pager a
{
    margin: 4px 3px;
}
.pager a
{
    border: 1px solid #aaa;
    padding: 3px 5px;
    text-align: center;
    text-decoration: none;
}

 

 

新加入 Action 方法與頁面

首先記得要在 Controller 程式中加入「MvcPaging」namespace 的 import,然後 controller 加入一個常式,預設的 Page Size,

private const int DefaultPageSize = 5;

接著建立新的 Action 方法與程式內容,

public ActionResult DefaultPage(
    int? page, 
    string city = "all", 
    string sortColumnName = "CustomerID", 
    string sort = "asc")
{
    int currentPageIndex = page.HasValue ? page.Value - 1 : 0;
    this.PrepareDropDownLists(city, sortColumnName, sort);
    this.FilterCity = city;
    this.SortColumnName = sortColumnName;
    this.SortType = sort;
    ViewBag.City = city;
    ViewBag.SortColumnName = sortColumnName;
    ViewBag.Sort = sort;
    using (NorthwindEntities db = new NorthwindEntities())
    {
        var query = db.Customers.Select(x => x);
        if (!city.Equals("all"))
        {
            query = query.Where("City == @0", city ?? "London");
        }
        query = query.OrderBy(string.Format("{0} {1}", sortColumnName, sort));
        ViewData.Model = query.ToPagedList(currentPageIndex, DefaultPageSize);
    }
    return View();
}

在上面的程式內容裡要注意到的就是黃色標示出來的程式,這只會輸出指定分頁的資料內容,而且輸出 IPagedList 也會同時將必要的分頁資料傳送到前端,接下來就是要看前端的內容,

@model IPagedList<MvcMSSQL.Models.Customer>
@{
    ViewBag.Title = "DefaultPage";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
 
@using (Html.BeginForm("DefaultPage", "Home", FormMethod.Post, new { id = "CustomerList" }))
{
    <h2>DefaultPage</h2>
    <fieldset>
        <br />
        City:@Html.Raw(ViewData["CityDDL"].ToString()) 
        Sort ColumnName:@Html.Raw(ViewData["ColumnDDL"].ToString())
        Sort Type:@Html.Raw(ViewData["SortDDL"].ToString())
        <input type="submit" value="submit" id="ButtonSubmit" />
        <input type="button" value="Reset" id="ButtonReset" />
    </fieldset>
    
    if (Model != null && Model.Count > 0)
    { 
        <div class="pager">
            @Html.Pager(Model.PageSize, Model.PageNumber, Model.TotalItemCount).Options(o => o
                .AddRouteValue("city", ViewBag.City)
                .AddRouteValue("sortColumnName", ViewBag.SortColumnName)
                .AddRouteValue("sort", ViewBag.Sort))
 
            Displaying @Model.ItemStart - @Model.ItemEnd of @Model.TotalItemCount item(s)
        </div>
        <br />
 
        <table>
            <tr>
                <th>
                    CompanyName
                </th>
                <th>
                    ContactName
                </th>
                <th>
                    ContactTitle
                </th>
                <th>
                    Address
                </th>
                <th>
                    City
                </th>
                <th>
                    Region
                </th>
                <th>
                    PostalCode
                </th>
                <th>
                    Country
                </th>
                <th>
                    Phone
                </th>
                <th>
                    Fax
                </th>
            </tr>
        @foreach (var item in Model)
        {
            <tr>
                <td>
                    @item.CompanyName
                </td>
                <td>
                    @item.ContactName
                </td>
                <td>
                    @item.ContactTitle
                </td>
                <td>
                    @item.Address
                </td>
                <td>
                    @item.City
                </td>
                <td>
                    @item.Region
                </td>
                <td>
                    @item.PostalCode
                </td>
                <td>
                    @item.Country
                </td>
                <td>
                    @item.Phone
                </td>
                <td>
                    @item.Fax
                </td>
            </tr>
        }
        </table>
    }
}
 
@if (false){ <script src="../../Scripts/jquery-1.7.2.min.js" type="text/javascript"></script> }
<script type="text/javascript">
    $(document).ready(function () {
 
        $('#ButtonReset').click(function () {
            location.href = '@Url.Action("DefaultPage", "Home")';
        });
    });
</script>

 

上面的前端 ViewPage 內容裡要特別注意的地方就是 Html.Pager() 的設定,節錄在下:

<div class="pager">
    @Html.Pager(Model.PageSize, Model.PageNumber, Model.TotalItemCount).Options(o => o
        .AddRouteValue("city", ViewBag.City)
        .AddRouteValue("sortColumnName", ViewBag.SortColumnName)
        .AddRouteValue("sort", ViewBag.Sort))
    Displaying @Model.ItemStart - @Model.ItemEnd of @Model.TotalItemCount item(s)
</div>

我們使用表單做資料的篩選,表單中的下拉選單值也是關係著顯示資料的內容,分頁時也需要維持原來的資料篩選條件,所以在 Html.Pager() 的設定就可以使用 AddRouteValue() 的方式讓 Pager 所產生的連結也加上這些篩選條件。

而下面的 Displaying …… Item(s),這一行則是顯示每個分頁所顯示的資料起迄,可以使用 Model.ItemStart 與 Model.ItemEnd 來顯示當前分頁的起迄索引值,而 Model.TotalItemCount 表示所有資料數量。

 

經過上面的設定之後所執行的分頁內容如下:

image

看一下在 Pager 上面每個分頁連結的連結內容:

image

最後的 page 是 Html.Pager() 會預設加入的,而前面的 city, sortColumnName, sort 則是我們剛才所加入的 RouteValue,如此一來就可以達到維持表單的資料篩選條件以及資料分頁的功能,

image

但其實將表單篩選資料值放在分頁連結的 HyperLink 中然後以 Get 的方式傳送到後端,這樣的方式並不是很適合,一旦日後這個表單的篩選條件增加或是要增加輸入關鍵字的條件,還是使用 Get 方式來處理就不是一個好方法了,表單的資料還是以 POST 的方式來傳送會比較好,我在以前也曾經以舊版的 MVCPaging 做了一個 PostPager,
ASP.NET MVC - 資料分頁(3) 自訂分頁功能列 MvcSimplePostPager
那一篇內容的作法比較著重在如何自己寫一個 Pager 出來然後搭配前端 jQuery 程式讓分頁有 POST 的功能,而新版的 MVCPaging 可以讓我們自訂分頁的 DisplayTemplate,所以可以使用 DisplayTemplate 來設計自己的分頁樣式,但這不是這一篇要說的內容,留待以後再說明如何自訂分頁的 DisplayTemplate,回到 POST 的功能上……,我們可以以簡單的方式在前端使用 jQuery 程式來達成這樣的功能需求,接下來就來看看要怎麼做。

 


使用 jQuery 讓 MVCPaging 可以 POST

後端 Controller 的 Action 方法內容,這一次就改寫成兩個 Action(),其中一個是接收 POST 資料的 Action(),

#region -- IndexPage --
public ActionResult PostPage()
{
    this.PrepareDropDownLists(this.FilterCity, this.SortColumnName, this.SortType);
    ViewBag.City = this.FilterCity;
    ViewBag.SortColumnName = this.SortColumnName;
    ViewBag.Sort = this.SortType;
    return View();
}
[HttpPost]
public ActionResult PostPage(
    int? page, 
    string city = "all", 
    string sortColumnName = "CustomerID", 
    string sort = "asc")
{
    int currentPageIndex = page.HasValue ? page.Value - 1 : 0;
    this.PrepareDropDownLists(city, sortColumnName, sort);
    this.FilterCity = city;
    this.SortColumnName = sortColumnName;
    this.SortType = sort;
    ViewBag.City = city;
    ViewBag.SortColumnName = sortColumnName;
    ViewBag.Sort = sort;
    using (NorthwindEntities db = new NorthwindEntities())
    {
        var query = db.Customers.Select(x => x);
        if (!city.Equals("all"))
        {
            query = query.Where("City == @0", city ?? "London");
        }
        query = query.OrderBy(string.Format("{0} {1}", sortColumnName, sort));
        ViewData.Model = query.ToPagedList(currentPageIndex, DefaultPageSize);
        return View();
    }
} 
#endregion

接下來看前端 ViewPage 的原始碼內容:

@model IPagedList<MvcMSSQL.Models.Customer>
@{
    ViewBag.Title = "PostPage";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
 
@using (Html.BeginForm("PostPage", "Home", FormMethod.Post, new { id = "CustomerList" }))
{
    <h2>PostPage</h2>
    <fieldset>
        <br />
        City:@Html.Raw(ViewData["CityDDL"].ToString()) 
        Sort ColumnName:@Html.Raw(ViewData["ColumnDDL"].ToString())
        Sort Type:@Html.Raw(ViewData["SortDDL"].ToString())
        <input type="submit" value="submit" id="ButtonSubmit" />
        <input type="button" value="Reset" id="ButtonReset" />
    </fieldset>
    
    if (Model != null && Model.Count > 0)
    { 
        <div class="pager">
            @Html.Pager(Model.PageSize, Model.PageNumber, Model.TotalItemCount)
            Displaying @Model.ItemStart - @Model.ItemEnd of @Model.TotalItemCount item(s)
        </div>
        <br />
 
        <table>
            <tr>
                <th>
                    CompanyName
                </th>
                <th>
                    ContactName
                </th>
                <th>
                    ContactTitle
                </th>
                <th>
                    Address
                </th>
                <th>
                    City
                </th>
                <th>
                    Region
                </th>
                <th>
                    PostalCode
                </th>
                <th>
                    Country
                </th>
                <th>
                    Phone
                </th>
                <th>
                    Fax
                </th>
            </tr>
            @foreach (var item in Model)
            {
                <tr>
                    <td>
                        @item.CompanyName
                    </td>
                    <td>
                        @item.ContactName
                    </td>
                    <td>
                        @item.ContactTitle
                    </td>
                    <td>
                        @item.Address
                    </td>
                    <td>
                        @item.City
                    </td>
                    <td>
                        @item.Region
                    </td>
                    <td>
                        @item.PostalCode
                    </td>
                    <td>
                        @item.Country
                    </td>
                    <td>
                        @item.Phone
                    </td>
                    <td>
                        @item.Fax
                    </td>
                </tr>
            }
        </table>
    }
}
 
@if (false)
{ <script src="../../Scripts/jquery-1.7.2.min.js" type="text/javascript"></script> }
<script type="text/javascript">
    $(document).ready(function () {
 
        $('#ButtonReset').click(function () {
            location.href = '@Url.Action("PostPage", "Home")';
        });
 
        $('.pager> a').each(function (i, item) {
            var page = $(item).attr('href').replace('/Home/PostPage?page=', '');
            $(item).attr('href', '#').click(function () { postPage(page); });
        });
 
    });
 
    function postPage(page) {
        var targetFormID = '#CustomerList';
        if ($(targetFormID).size() > 0) {
            $('<input>')
                .attr({ type: 'hidden', id: 'page', name: 'page', value: page })
                .appendTo($(targetFormID));
            $(targetFormID).submit();
        }
    };
 
</script>

在前端 ViewPage 中的 Html.Pager(),我們不再加入 RouteValue 內容,而把 POST 的動作改用 jQuery 的程式來做處理,我們置換 Pager 裡每個分頁連結的 HyperLink 內容,並且綁定 click 事件,讓點擊每個分頁連結都是送出 POST 動作,

$(document).ready(function () {
    $('#ButtonReset').click(function () {
        location.href = '@Url.Action("PostPage", "Home")';
    });
    $('.pager> a').each(function (i, item) {
        var page = $(item).attr('href').replace('/Home/PostPage?page=', '');
        $(item).attr('href', '#').click(function () { postPage(page); });
    });
});
function postPage(page) {
    var targetFormID = '#CustomerList';
    if ($(targetFormID).size() > 0) {
        $('<input>')
            .attr({ type: 'hidden', id: 'page', name: 'page', value: page })
            .appendTo($(targetFormID));
        $(targetFormID).submit();
    }
};

這樣一來就完成分頁使用 POST 傳送篩選資料與分頁索引到後端的處理。

 

執行結果:

一開始進入 PostPage 頁面

image

不做任何篩選條件的選擇,送出表單資料以顯示資料與分頁內容,

image

分頁的連結內容

image

點擊並顯示分頁的第二頁內容

image

點擊分頁連結所 POST 到後端的內容

image

更動表單篩選資料的分頁內容

image

顯示其他分頁以及 POST 的內容

image

image

 

最後還是來一段無聲影片的操作示意!

 

下次再來說明怎麼把這一篇的網頁內容以 AJAX 的方式來呈現。


 

參考連結:

ASP.NET MVC 3 資料分頁 - MvcPaging 2.0.0 Release

Dynamic LINQ + Entity Framework - Part.3:ASP.NET MVC 應用

Martijn Boland - MvcPaging 2.0

NuGet Gallery - MvcPaging

 

以上

5 則留言:

  1. 作者已經移除這則留言。

    回覆刪除
  2. 請問在分頁裡面的資料如果要Post回去要怎麼處理呢?

    目前查過好像是因為無法自動繫結所以取得都是null

    因為想取得使用者打勾的某幾筆資料,新增到另一個資料表內

    回覆刪除
    回覆
    1. 有關分頁資料勾選後的處理,這都是需要開發人員自己做處理,
      可以使用 AJAX 的方式來處理分頁,這樣分頁的執行就不會影響到頁面本身,
      那麼勾選資料的紀錄一樣也是使用 AJAX 處理,勾選資料後以 JAVASCRIPT 做記錄處理,
      有關這部份的處理在我的部落格裡還沒有相關類似的文章。

      刪除
    2. 謝謝您的回覆,我試著寫看看

      刪除

提醒

千萬不要使用 Google Talk (Hangouts) 或 Facebook 及時通訊與我聯繫、提問,因為會掉訊息甚至我是過了好幾天之後才發現到你曾經傳給我訊息過,請多多使用「詢問與建議」(在左邊,就在左邊),另外比較深入的問題討論,或是有牽涉到你實作程式碼的內容,不適合在留言板裡留言討論,請務必使用「詢問與建議」功能(可以夾帶檔案),謝謝。