《Pro ASP.NET MVC 3 Framework》學習筆記之十四【示例項目SportsStore】


前面給SportsStore添加了分頁的功能。接着我們添加導航控制,通過左邊的菜單來分類顯示數據。

首先修改SportsStore.WebUI.Models下的ProductsListViewModel.cs,增加一個CurrentCategory屬性public string CurrentCategory{get;set;}。
接着修改List action方法,能夠通過傳入的CurrentCategory來過濾查詢的產品列表。修改后的List action方法如下所示:

public ViewResult List(string category, int page = 1) { 

            ProductsListViewModel viewModel = new ProductsListViewModel
            {
                Products = repository.Products.Where(p => category == null || p.Category == category)
                .OrderBy(p => p.ProductID)
                .Skip((page - 1) * PageSize)
                .Take(PageSize),
                PagingInfo = new PagingInfo
                {
                    CurrentPage = page,
                    ItemsPerPage = PageSize,
                    TotalItems = category == null ? repository.Products.Count() : repository.Products.Where(e => e.Category == category).Count()
                }
            };
            return View(viewModel);
}

通過.where擴展方法我們進行了一個縱向的篩選,將指定Category的Product篩選出來。

重定義URL組合

之前我們看到了一種URl是這樣的/?category=Soccer.在webform里面這個是常見的,但是在MVC里面,可以有更加優雅的方式來代替。我們需要修改global.asax.cs里面的RegisterRoutes方法。如下所示:

        public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

routes.MapRoute(null,
"",//匹配空的URL
new { controller = "Product", action = "List", category = (string)null, page = 1 }
);

routes.MapRoute(null,
"Page{page}",
new { controller = "Product", action = "List", category = (string)null },
new { page = @"\d+" }//約束:頁面必須是數字
);

routes.MapRoute(null,
"{category}",//匹配 /Football or /AnythingWithNoSlash
new { controller = "Product", action = "List", page = 1 }
);

routes.MapRoute(null,
"{category}/Page{page}",//匹配 /Football/Page567
new { controller = "Product", action = "List" },//默認
new { page = @"\d+" }//必須為數字

);
routes.MapRoute(null,
"{category}/Page{page}",//匹配/Football/Page567
new { controller = "Product", action = "List" },//默認
new { page = @"\d+" }//約束
);

routes.MapRoute(
"Default", // 路由名稱
"{controller}/{action}/{id}", // 帶有參數的 URL
new { controller = "Product", action = "List", id = UrlParameter.Optional } // 參數默認值
);

}

上面的集中組合分別代表以下集中URL的組合:


ASP.NET的路由系統(Routing System)被MVC用來處理來自客戶端的請求,但是也會請求向外輸出的符合我們URL組合的URL,以及我們能嵌入在web pages里面輸出的URL。這樣就能夠保證URL在我們程序里面是一直不變的。對於路由這塊,后面的章節有詳細的講解,所以如果我們這里不太明白,也沒關系。

Url.Action方法是我們創建對外輸出鏈接(outgoing links)最方便的一種方式,在List.cshtml里面已經使用過。由於我們需要添加支持對Category的篩選並且把數據傳遞給helper方法。如下所示:

@model SportsStore.WebUI.Models.ProductsListViewModel
@{
ViewBag.Title = "Products";
}
<h2>
Product List</h2>
@foreach (var p in Model.Products)
{
@* <div class="item">
<h3>@p.Name</h3>
@p.Description
<h4>@p.Price.ToString("c")</h4>
</div> *@
Html.RenderPartial("ProductSummary", p);
}
<div class="pager">
@Html.PageLinks(Model.PagingInfo, x => Url.Action("List", new { page = x, category = Model.CurrentCategory }))
</div>

分頁的鏈接是形如:http://<myserver>:<port>/Page2 的,這個表示顯示所有Product里面的第2頁。http://<myserver>:<port>/Chess/Page2這個就會只顯示Category為Chess的Product。

綁定一個Category的導航菜單

我們需要顯示一個導航的菜單共用戶選擇,然后顯示對應的Product列表。並且我們會在多個Controller里面使用Category的列表,所以我們需要一個獨立的,可重用的這樣一個Category的列表。在ASP.NET MVC框架里面有子Action(child actions)的概念,對於創建一個可重用的導航控制菜單來說非常合適。child actions需要使用HTML的輔助方法RenderAction,它能夠讓我們包含一個對應view的action方法的輸出。這里,我們可以創建一個新的Controller:NavController,並創建一個Menu的action方法,用來呈現一個導航菜單,並將來自action方法的輸出注入到layout里面。

添加NavController,如下所示:

    public class NavController : Controller
{
private IProductsRepository repository;
public NavController(IProductsRepository repo)
{
repository = repo;
}

public PartialViewResult Menu(string category=null)
{
ViewBag.SelectedCategory = category;//這里使用的ViewBag將選中的Category傳遞給View。
IEnumerable<string> categories = repository.Products
.Select(x => x.Category)
.Distinct()
.OrderBy(x => x);
return PartialView(categories);
}
}

因為要在所有的頁面都使用這個導航菜單,所以需要注入到_layout里面,修改后的_layout如下所示:

<!DOCTYPE html>
<html>
<head>
<title>@ViewBag.Title</title>
<link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
<script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
</head>
<body>
<div id="header">
<div class="title">
Sports Store</div>
</div>
<div id="categories">
@{Html.RenderAction("Menu", "Nav");}
</div>
<div id="content">
@RenderBody()
</div>
</body>
</html>

Note:RenderAction()跟RenderPartial()一樣,都是直接將內容寫入Response Stream。

也就是意味着返回值是void,因此也就不能使用Razor里面的“@”標簽,而是將它包括在@{}這樣一個閉合的塊里面。

如果你不喜歡這樣,可以使用action方法來替代。
接着添加Menu的view,在Menu Controller上右鍵添加View,注意:這里的view是一個Partial view,類似webform里面的customer control.如下所示:

@model IEnumerable<string>
@{
Layout = null;
}
@Html.ActionLink("Home", "List", "Product")
@foreach (var link in Model)
{
@Html.RouteLink(link, new { controller = "Product", action = "List", category = link, page = 1 },
new { @class = link == ViewBag.SelectedCategory ? "selected" : null })
}

接着添加一個樣式,如下所示:

View Code
DIV#categories A 
{
font: bold 1.1em "Arial Narrow","Franklin Gothic Medium",Arial; display: block;
text-decoration: none; padding: .6em; color: Black;
border-bottom: 1px solid silver;
}
DIV#categories A.selected { background-color: #666; color: White; }
DIV#categories A:hover { background-color: #CCC; }
DIV#categories A.selected:hover { background-color: #666; }

在Menu的View里面有這樣一個@classe用法,我們在匿名對象里使用@class作為一個新的參數傳遞給RouteLink方法。這里的“@”不是一個Razor的tag,我們使用了C#里面的一個功能,通過給class添加一個@符合來避免html里面的class和C#里面定義類的關鍵字class發生沖突。如果這里沒有加上@符合,就會被C#編譯器認為是定義了一個類。當我們添加了@符合后,編譯就知道我們是在匿名類型里面添加的一個參數名為class的參數。這也正好達到了我們的期望。

今天的筆記就到這里。后面會創建簡單的購物車,總得來說這個項目實例主要是讓我們能夠對MVC有一個實際的認識,項目里面一些具體實現,比如分頁是全部讀出來后分頁的。這可能不符合實際的要求,但這不重要。重要的是,通過這個實例對MVC有整體的認識,通過具體操作能夠對以后的實際項目的開發積累點經驗。如果你是跟着我的筆記操作,出現什么問題,請留言!我是自己已經操作了一遍后,倒過來寫筆記的,所以中間的一些步驟我會選擇性的舍棄。

晚安!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM