第六話完了,我的項目只不過構建了大體的樣子,接下來我們需要完成導航部分購物車部分,訂單部分等。只有這些模塊搞完,我們的購物流程項目才算大體的搞完。接下來,就從我們的導航開始吧!
添加導航
如果在我們的項目應用導航展示給用戶,我們應該做一下的事情:
- 加強我們的模型(ProductsListViewModel),加強之后的模型必須過濾商品的屬性。
- 重構我們的URL,修改我們路由機制。
- 創建類別列表,顯示在網站的側邊欄里。
加強我們的模型(ProductsListViewModel),我們需要把不同類別展示在網站的側邊欄里讓用戶一目明了。ProductsListViewModel模型修改如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using SportsStore.Domain.Entities; namespace SportsStore.WebUI.Models { public class ProductsListViewModel { public IEnumerable<Product> Products { get; set; } public PagingInfo PagingInfo { get; set; } //添加CurrentCategory屬性 public string CurrentCategory { get; set; } } }
模型(ProductsListViewModel)我們已經添加了屬性進來,下來我們需要修改我們的ProductController(控制器)里的List(Action)方法,我們需要在List方法使用我們添加的屬性按照分類過濾商品對象。修改ProductContrller如下:

using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using SportsStore.Domain.Abstract; using SportsStore.Domain.Concrete; using SportsStore.WebUI.Models; namespace SportsStore.WebUI.Controllers { public class ProductController : Controller { public int PageSize = 4; //設置一頁顯示多少商品 private IProductRepository repository; public ProductController(IProductRepository productReposittory) { this.repository = productReposittory; } //返回一個視圖 public ViewResult List(string category, int page = 1) { ProductsListViewModel viewModel = new ProductsListViewModel { Products = this.repository.Products .Where(h => category == null || h.Category == category) .OrderBy(h => h.ProductID) .Skip((page - 1) * PageSize) .Take(PageSize), PagingInfo = new PagingInfo { CurrentPage = page, ItemsPerPage = PageSize, TotalItems = this.repository.Products.Count() }, CurrentCategory = category }; return this.View(viewModel); } } }
上面的代碼,我們添加了一個新的分類參數,我們做到這里即便我們做了稍微的改變運行一下我們的程序,自己試着拼一個URL分類的字符串。運行如下圖1.
圖1.可以看出我們拼的字符串可以正確的顯示出我們拼的分類列表。但是像Http://localhost:XXXX/?category=people的連接我相信大家都是不喜歡的。所以我們要從新定義一套路由機制來改變這種URL訪問的方式。然后在我們的Global.asax文件中配置一下幾條路由,代碼如下:

using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Routing; using SportsStore.WebUI.Infrastructure; using SportsStore.WebUI.Binders; using SportsStore.Domain.Entities; namespace SportsStore.WebUI { // 注意: 有關啟用 IIS6 或 IIS7 經典模式的說明, // 請訪問 http://go.microsoft.com/?LinkId=9394801 public class MvcApplication : System.Web.HttpApplication { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new HandleErrorAttribute()); } 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}", //匹配像 /Page2,/page123,除了/PageXYZ new { controller = "Product", action = "List", category = (string)null }, new { page = @"\d+" }); //約束:頁面數值必須是數字 routes.MapRoute(null, "{category}", //匹配類別 new { controller = "Product", action = "List", page = 1 }); routes.MapRoute(null, "{category}/Page{page}", //配置 類別和分頁 new { controller = "Product", action = "List" }, new { page = @"\d+" }); //約束:頁面數值必須是數字 routes.MapRoute(null, "{controller}/{action}"); //添加一條路由 routes.MapRoute( null, "Page{page}", new { controller = "Product", action = "List" } ); routes.MapRoute( "Default", // 路由名稱 "{controller}/{action}/{id}", // 帶有參數的 URL new { controller = "Product", action = "List", id = UrlParameter.Optional } // 參數默認值 ); } protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RegisterGlobalFilters(GlobalFilters.Filters); RegisterRoutes(RouteTable.Routes); //注冊路由 ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory()); ModelBinders.Binders.Add(typeof(Cart), new CartModelBinder()); } } }
總結一下我們配置的這些路由機制,如下表:
URL | 引導 |
/ | 展示列表第一頁的所有商品信息 |
/page2 | 展示列表指定頁面(這里指第2頁)的商品信息 |
/Soccer | 展示特定分類第一頁的商品信息 |
/Soccer/Page2 | 展示特定分類頁特定頁面(這里指第二頁)的商品信息 |
/Anything/Else | 調用任何控制器里的的其他方法 |
Asp.Net路由系統使用在MVC來處理客戶端的請求,外來的URL請求要是有符合我們配置的URL,就會訪問到我們Web程序相應的頁面。在Razor引擎的寫法里,大家都應該知道URl.Action方法是最常見的添加外鏈的方法,那么下來就在我們的List.cshtml商品展示頁面添加我們的分類信息,具體代碼如下:
@model SportsStore.WebUI.Models.ProductsListViewModel @{ ViewBag.Title = "Product List"; } @foreach (var Pro in Model.Products) { Html.RenderPartial("ProductSummary", Pro); } <div class="pager"> @Html.PageLinks(Model.PagingInfo, x => Url.Action("List", new { Page = x ,category = Model.CurrentCategory})) </div>
接下來我們需要構造一個類別的導航菜單,在我們的MVC Web項目里的Controllers文件夾右鍵添加"控制器",命名"NavController",然后干掉VS自動創建好的Index 方法(Action),然后定義我們的方法,代碼如下:
using System.Web.Mvc; namespace SportsStore.WebUI.Controllers { public class NavController : Controller { public string Menu() { return "Hello from NavController"; } } }
我們的Menu方法返回的字符串,但是我們想讓我們的類別出現在任何頁面,所以我們要配置我們的模版(_Layout.cshtml),使它調用我們的顯示導航菜單的方法,所以我們修改我們的Layout.cshtml的代碼如下:
<!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">SPOPTS STORE</div> </div> <div id="categories">@{Html.RenderAction("Menu", "Nav");}</div> <div id="content"> @RenderBody() </div> </body> </html>
我們現在已經在模版中給我們的導航菜單分配了一塊展示的地方,現在運行一下我們的Mvc Web程序,結果如下圖2.
圖2.現在我們就可以去實現這個展示導航菜單的位置了,不能讓這個展示導航的地方老是顯示一句我們測試的字符串,接下來我們就生成我們的導航列表,但是我們有不願意生成控制器類(NavController)的URL,所以我們將嘗試用一個輔助方法來搞View(視圖),所以需要Menu Action(方法)是創建一個分裂的列表,修改控制器("NavController")代碼如下:

using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using SportsStore.Domain.Abstract; namespace SportsStore.WebUI.Controllers { public class NavController : Controller { private IProductRepository repository; public NavController(IProductRepository repo) { this.repository = repo; } public PartialViewResult Menu() { IEnumerable<string> categories = this.repository.Products .Select(h => h.Category) .Distinct() .OrderBy(h => h); return this.PartialView(categories); } } }
接下來我們需要創建一個局部視圖,為什么是局部視圖,因為我們的導航菜單是我們頁面整體的一部分,所以我們就用局部視圖,在Menu Action(方法)上右鍵添加視圖,具體如下圖3。
圖3.我們還是創建的強類型視圖模型,我們的模型類類型選擇的是IEnumerable<string>,創建好我們的局部視圖Menu后,修改他的內容如下:
@model IEnumerable<string> @{ this.Layout = null; } @Html.ActionLink("Home","List","Product") @foreach (var item in Model) { @Html.RouteLink(item, new { controller = "Product", action = "List", category = item, page = 1 }); }
首先我們添加一個Home(首頁)的連接使用的是ActionLink,這個連接會展示出我們所有商品信息。這種的URL請求機制我們在Global.asax文件中已經配置過來。然后我們使用循環列舉了所有的列表使用的RouteLink(這個和ActionLinke類似),但是這個需要我們供給一組類似"名稱/值(Name/Value)"對,請求時來從路由配置(具體到路由的東西后續分享)。到這里我們在跑下我們的Web程序看看!如下圖4.
圖4.哦,我們應該還需要一個"高亮"的效果,就是當我們/用戶點擊到某個分裂的時候,當前分類應該處於選擇狀態的時候,有"高亮"效果,我們修改一下我們修改一下我們的Menu Action(方法)及對應的前台代碼。
修改Menu Action(方法)具體如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using SportsStore.Domain.Abstract; namespace SportsStore.WebUI.Controllers { public class NavController : Controller { private IProductRepository repository; public NavController(IProductRepository repo) { this.repository = repo; } public PartialViewResult Menu(string category = null) { this.ViewBag.SelectedCategory = category; IEnumerable<string> categories = this.repository.Products .Select(h => h.Category) .Distinct() .OrderBy(h => h); return this.PartialView(categories); } } }
修改頁面Menu.cshtml如下(選擇類別的高亮實現):
@model IEnumerable<string> @{ this.Layout = null; } @Html.ActionLink("Home","List","Product") @foreach (var item in Model) { @Html.RouteLink(item, new { controller = "Product", action = "List", category = item, page = 1 }, new { @class = item == this.ViewBag.SelectedCategory ? "selected" : null }); }
注:具體實現高亮的地方,代碼都表明使用了紅色粗體。
接下來運行我們的web程序如下圖5.
圖5.這里我們的高亮效果已定是OK的了。接下來我們似乎有出現一個新的問題,就是在我們某些類別頁面存在分頁沒有商品的信息,如下圖6.
圖6.為什么呢!還記得前面我們搞分頁的時候,依據的商品的總數量,與分類好無瓜葛,所以導致現在這種局面也是必然。所以當用戶點擊某些類別的具體某些頁面時,沒有足夠的商品信息展示出來就出現空白頁面,接下來就搞這個問題吧!問題的根源弄出來,那么我們是不是應該在商品展示List Action(方法)上動些手腳,讓分頁的時候把類別也考慮進來,所以修改ListAction(方法)如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using SportsStore.Domain.Abstract; using SportsStore.Domain.Concrete; using SportsStore.WebUI.Models; namespace SportsStore.WebUI.Controllers { public class ProductController : Controller { public int PageSize = 4; //設置一頁顯示多少商品 private IProductRepository repository; public ProductController(IProductRepository productReposittory) { this.repository = productReposittory; } //返回一個視圖 public ViewResult List(string category, int page = 1) { ProductsListViewModel viewModel = new ProductsListViewModel { Products = this.repository.Products .Where(h => category == null || h.Category == category) .OrderBy(h => h.ProductID) .Skip((page - 1) * PageSize) .Take(PageSize), PagingInfo = new PagingInfo { CurrentPage = page, ItemsPerPage = PageSize, TotalItems = category == null ? this.repository.Products.Count() : this.repository.Products.Where(h => h.Category == category).Count() }, CurrentCategory = category }; return this.View(viewModel); } } }
這樣修改后,當我們選擇某些特定的類別的時候,就返回特定類別的商品的總數,就會正確的分頁,避免之前的空白頁面了!到時是不是呢,我們還是剛才選擇的那個分類運行下我們的web項目,如下圖7.
圖7.這里已經沒有什么在說的了,證明我們的修改是OK的。
接下是相對於之前比較復雜的購物車,說是復雜就復雜,要說不復雜也就不復雜了,直白點"購物車不就那幾點事,無非就是計算我們的商品價格,無非就是支付,無非就是跟商品頁面的交互,...",我們項目的購物車的流程如下圖8.
圖8.具體實現購物車的東東后續在繼續,這里就引下!我們本次也就搞了一個導航菜單,就先實戰到這里,文章要是存在什么地方的錯誤或者描述不清楚的地方還請路過的前輩們多多批評指教,大家共同學習哈!