2012年10月5日 星期五

ASP.NET MVC 資料分頁與 Route - Part.2


前一篇「ASP.NET MVC 資料分頁與 Route - Part.1」說明了資料分頁的 Route 設定方式,然而資料分頁並不會這麼單純地只有把全部資料拿出來做分頁,最常遇到的情況就是產品資料依據分類來做顯示,而且還要可以分頁,這一次就是要來說明產品資料依據分類來做顯示並且資料分頁的情況下如何做 Route 設定。

 


操作示範使用 ASP.NET MVC 4.0 開發,範例資料庫使用「Northwind」,分頁功能使用「MvcPaging 2.0」

初學習 ASP.NET MVC 的 Route (路由)設定,建議先詳讀 MSDN 的「ASP.NET 路由」:

http://msdn.microsoft.com/zh-tw/library/cc668201(v=vs.100).aspx

MvcPaging 2.0

http://blogs.taiga.nl/martijn/2012/04/23/mvcpaging-2-0/

 

先看一下做好的頁面,

image

這一次多了一個產品分類的下拉選單,用下拉選單來顯示各分類下的資料列表,

在預設的 Route 設定下,我們所使用的 URL 格式會是以下的樣式:

  • http://localhost:5511/Product/List?category=1
  • http://localhost:5511/Product/List?category=1&page=2
  • http://localhost:5511/Product/List?category=all
  • http://localhost:5511/Product/List?category=all&page=3

這樣的 URL 樣式並沒有問題,只是這樣的格式並不是很直覺,所以我們可以新增加 Route 設定來設定 URL 樣式,讓 URL 的格式可以更加直覺也比較漂亮一些。

以下是我們希望顯示並使用的 URL 樣式:

  • http://localhost:5511/Product/Category
  • http://localhost:5511/Product/Category/all
  • http://localhost:5511/Product/Category/all/Page/2
  • http://localhost:5511/Product/Category/8/Page
  • http://localhost:5511/Product/Category/8/Page/2

 

Route 設定:

這一次的 Route 設定會增加三個,由上到下分別是:

「沒有輸入分類代碼、分頁碼」「輸入分類代碼、不輸入分頁碼」「有輸入分類代碼、分頁碼」

routes.MapRoute(
    "ProductList_Default",                                                      // 路由名稱
    "Product/Category",                                                         // URL 及參數
    new { controller = "Product", action = "List", Category = "all", page = 1 } // 參數預設值
);
 
routes.MapRoute(
    "ProductList_Category",   // 路由名稱
    "Product/Category/{Category}",              // URL 及參數
    new { controller = "Product", action = "List", Category = "all", page = 1 } // 參數預設值
);
 
routes.MapRoute(
    "ProductList_Category_Page",                                                // 路由名稱
    "Product/Category/{Category}/Page/{page}"// URL 及參數
    new { controller = "Product", action = "List", Category = "all", page = 1 } // 參數預設值
);

 

Controller / Action 方法:

因為這次有使用到下拉選單,所以會使用表單的方式將產品分類的值 POST 到後端,那因為使用 POST 後所回來的 URL 也希望一樣有 Route 格式,所以會在接收 POST 的 Action方法中的最後使用 RedirectToRoute() 送到之前的 List() Action 方法來產出分頁資料,

/// <summary>
/// List
/// </summary>
/// <param name="category"></param>
/// <param name="page"></param>
/// <returns></returns>
public ActionResult List(string category = "all", int page = 1)
{
    int currentPageIndex = page < 0 ? 0 : page - 1;
 
    int categoryID = 1;
 
    ViewBag.CategorySelectList = int.TryParse(category, out categoryID)
        ? this.CategorySelectList(categoryID.ToString())
        : this.CategorySelectList("all");
 
    var query = productService.GetAll();
 
    if (!string.IsNullOrWhiteSpace(category) && !category.Equals("all", StringComparison.OrdinalIgnoreCase))
    {
        query = query.Where(x => x.CategoryID == categoryID);
    }
    query = query.OrderBy(x => x.CategoryID).ThenBy(x => x.ProductID);
 
    ViewData.Model = query.ToPagedList(currentPageIndex, DefaultPageSize);
 
    ViewBag.Category = category;
    ViewBag.Page = currentPageIndex;
 
    return View();
}
 
[HttpPost]
public ActionResult List(string category)
{
    return RedirectToRoute("ProductList_Category_Page", new
    {
        Controller = "Product",
        Action = "List",
        category = category,
        page = 1
    });
} 

下拉選單的產生

/// <summary>
/// CategorySelectList
/// </summary>
/// <param name="selectedValue"></param>
/// <returns></returns>
public List<SelectListItem> CategorySelectList(string selectedValue = "all")
{
    List<SelectListItem> items = new List<SelectListItem>();
    items.Add(new SelectListItem() 
    { 
        Text = "All Category", 
        Value = "all", 
        Selected = selectedValue.Equals("all", StringComparison.OrdinalIgnoreCase) 
    });
 
    var categories = categoryService.GetAll().OrderBy(x => x.CategoryID);
 
    foreach (var c in categories)
    {
        items.Add(new SelectListItem() 
        { 
            Text = c.CategoryName, 
            Value = c.CategoryID.ToString(), 
            Selected = selectedValue.Equals(c.CategoryID.ToString())
        });
    }
    return items;
}

 

View:

View 的內容要特別注意的就是 Html.Pager() 的設定,前一篇的範例中並沒有特別去做任何的修改,但是這一篇是有多出產品分類的條件,而這一個條件也必須要讓 MvcPaging 知道,而這個讓 MvcPagin 知道的動作就是在 View 裡面的 Html.Pager() 增加 RouteValue,

<div class="pager">
    @Html.Pager(Model.PageSize, Model.PageNumber, Model.TotalItemCount).Options(o => 
        o.AddRouteValue("category", ViewBag.Category))
    Displaying @Model.ItemStart - @Model.ItemEnd of @Model.TotalItemCount item(s)  
</div>

Options(o => o.AddRouteValue("category", ViewBag.Category))

要增加的就是上面的這一段設定,增加名稱為 category 的 RouteValue。

完整的 View 內容如下:

@model IPagedList<MvcPagedBrowserHash.Models.Products>
@{
    ViewBag.Title = "List";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
@using (Html.BeginForm("List", "Product", FormMethod.Post, new { id = "ProductList" }))
{
 
    <h2>Products List</h2>
    <br />
    <fieldset>
        <br />
        Category:@Html.DropDownList("category", (IEnumerable<SelectListItem>)ViewBag.CategorySelectList)
        <input type="submit" value="submit" id="ButtonSubmit" />
        <input type="button" value="Reset" id="ButtonReset" />
    </fieldset>
    
    <div class="pager">
        @Html.Pager(Model.PageSize, Model.PageNumber, Model.TotalItemCount).Options(o => 
            o.AddRouteValue("category", ViewBag.Category))
        Displaying @Model.ItemStart - @Model.ItemEnd of @Model.TotalItemCount item(s)  
    </div>
    <br />
    <table>
        <tr>
            <th>ProductID
            </th>
            <th>ProductName
            </th>
            <th>SupplierID
            </th>
            <th>CategoryID
            </th>
            <th>QuantityPerUnit
            </th>
            <th>UnitPrice
            </th>
            <th>UnitsInStock
            </th>
            <th>UnitsOnOrder
            </th>
            <th>ReorderLevel
            </th>
            <th>Discontinued
            </th>
        </tr>
 
        @foreach (var item in Model)
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.ProductID)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.ProductName)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.SupplierID)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.CategoryID)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.QuantityPerUnit)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.UnitPrice)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.UnitsInStock)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.UnitsOnOrder)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.ReorderLevel)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Discontinued)
                </td>
            </tr>
        }
 
    </table>
}
 
@section scripts{
    <script type="text/javascript">
        $(document).ready(function () {
            $('#ButtonReset').click(ButtonResetEventHandler);
        });
 
        var ButtonResetEventHandler = function (){
            location.href = '@Url.RouteUrl("ProductList_Default", new { category = "all", page = 1 })';
        };
    </script>
}

 

這樣一來就大致完成了我們要的需求功能,下面就一一輸入 URL 然後與 Route Debugger 的內容進行比對,以了解 Route 匹配的狀況,

 

http://localhost:5511/Product/Category

image

 

http://localhost:5511/Product/Category/all

image

 

http://localhost:5511/Product/Category/all/Page/7

image

 

http://localhost:5511/Product/Category/1/Page

image

 

http://localhost:5511/Product/Category/8/Page/2

image

 

這一篇就大致完成了!

 


假如我們今天想要讓原本顯示使用的 CategoryID 改為使用 CategoryName 呢?

使用 CategoryName 絕對是會比使用 CategoryID 更加的直覺,但是這個該怎麼做呢?

其實這並不會難做,不過還要有一些條件必須成立的情況下才有辦法做到,

至於詳細實作的內容為何呢?請看下回 ……

 

系列文章,下一篇:

ASP.NET MVC 資料分頁與 Route - Part.3

 

以上

沒有留言:

張貼留言

提醒

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