SportsStore是《精通ASP.NET MVC3框架(第三版)》中演示的MVC项目,在该项目中涵盖了MVC的众多方面,包括:使用DI容器、URL优化、导航、分页、购物车、订单、产品管理、图像上传......是不错的MVC实践项目,但该项目不是放在多层框架下开发的,离真实项目还有一段距离。本系列将尝试在多层框架下实现SportsStore项目,并用自己的方式实现一些功能。
本篇为系列第五篇,包括:
■ 8、导航
8、导航
创建NavController,派生于BaseController:
using System.Collections.Generic; using System.Linq; using System.Web.Mvc; using MySportsStore.IBLL; using Ninject; namespace MySportsStore.WebUI.Controllers { public class NavController : BaseController { [Inject] public IProductService ProductService { get; set; } public NavController() { this.AddDisposableObject(ProductService); } public PartialViewResult Menu(string category = null) { ViewBag.SelectedCategory = category; IEnumerable<string> categories = ProductService.LoadEntities(p => true).Select(p => p.Category).Distinct().OrderBy(p => p); return PartialView(categories); } } }
为什么有category参数?
为了让当前点击、选中的分类高亮显示。这里的category轨迹是:
→前端视图点击分类名称,并把分类名称赋值给路由变量category
→Nav控制器的Menu()方法接收到category,把其放到ViewBag中,再次传回前端视图
→前端视图在遍历所有分类名称的时候,如果当下分类名称与ViewBag中的相同,就为当下分类添加一个CSS,即高亮显示
需要为category赋上一个默认值,因为在点击分类名称之前,category为null。
在Nav/Menu.cshtml部分视图中,需要把category和page传递到Product控制器中的Nav()方法中:
@model IEnumerable<string> @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}) }
在Views/Shared下的_Layout.cstml中显示加载分类部分视图:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width" /> <title>@ViewBag.Title</title> <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" /> </head> <body> <div id="header"> <div class="title">体育用品商店</div> </div> <div id="categories"> @{Html.RenderAction("Menu","Nav");} </div> <div id="content"> @RenderBody() </div> @Scripts.Render("~/bundles/jquery") @RenderSection("scripts", required: false) </body> </html>
根据点击分类传递的路由参数,在Product控制器的Nav方法中,还需要考虑到category,再对集合进行筛选。并且,还要考虑当没有点击导航分类名称的时候,category为null:
using System.Web.Mvc; using MySportsStore.IBLL; using MySportsStore.WebUI.Models; using Ninject; namespace MySportsStore.WebUI.Controllers { public class ProductController : BaseController { [Inject] public IProductService ProductService { get; set; } public ProductController() { this.AddDisposableObject(ProductService); } public int PageSize = 4; public ViewResult List(string category, int page = 1) { int totalCount = 0; ProductsListViewModel viewModel = new ProductsListViewModel() { Products = ProductService.LoadPageEntities(p => category == null ? true : p.Category == category, p => p.Id, PageSize, page, out totalCount, true), PagingInfo = new PagingInfo(){CurrentPage = page, ItemsPerPage = PageSize, TotalItems = category == null ? ProductService.Count(p => true) : ProductService.Count(p => p.Category == category)}, CurrentCategory = category }; return View(viewModel); } } }
以上,在视图模型ProductsListViewModel中添加了CurrentCategory属性,这个属性值是要交给分页的:
using System.Collections.Generic; using MySportsStore.Model; namespace MySportsStore.WebUI.Models { public class ProductsListViewModel { public IEnumerable<Product> Products { get; set; } public PagingInfo PagingInfo { get; set; } public string CurrentCategory { get; set; } } }
在Product/List.cshtml视图的分页部分,需要把CurrentCategory值赋值给路由变量category:
@model MySportsStore.WebUI.Models.ProductsListViewModel @{ ViewBag.Title = "List"; Layout = "~/Views/Shared/_Layout.cshtml"; } @foreach (var item in Model.Products) { Html.RenderPartial("PrductSummary", item); } <div class="pager"> @Html.PageLinks(Model.PagingInfo, x => Url.Action("List", new {page = x, category = Model.CurrentCategory})) </div>
运行:
为了让URL更好看,需要考虑如下情境下的URL:
● 运行默认页的时候
● 点击分页的时候
● 点击导航分类的时候
● 点击导航分类后,再点击分页的时候
调整URL为:
routes.MapRoute( null, "", //匹配空的URL,如"/" new{controller = "Product", action = "List", category = (string)null, page = 1} ); routes.MapRoute( null, "Page{page}", //匹配"/Page1",当点击分页的时候 new {controller = "Product", action = "List", category = (string)null}, new {page = @"d+"} //约束page为数字 ); routes.MapRoute( null, "{category}", //匹配 "/Soccer",当点击导航分类的时候 new {controller = "Product", action = "List", page = 1} ); routes.MapRoute( null, "{category}/Page{page}", //匹配"/Soccer/Page1",当点击导航分类,在点击分页的时候 new {controller = "Product", action = "List"}, new {page = @"d+"} );
源码在这里。
“MVC项目实践,在三层架构下实现SportsStore”系列包括: