ASP.NET MVC2 第五章Ⅰ

§5  SportsStore: Navigation and Shopping Cart

In this Chapter ,you’ll learn how to do the following

  • Use the Html.RenderAction() helper method to create reusable, unit testable,templated controls
  • Validate form submissions
  • Create a custom model binder that separates out the concern of storing the visitor’s shopping cart—allowing your action methods to be simpler
  • Apply your DI infrastructure to implement a pluggable framework for handling
    completed orders

§5.1 Adding Navigation Controls

Now let visitors navigate products by category

  • Enhance ProductsController’s List action so that it can filter by category.
  • Improve your routing configuration so that each category has a “clean” URL.
  • Create a category list to go into the site’s sidebar, highlighting the current product category and linking to others. This will use the Html.RenderAction() helper method.

§5.1.1 Filtering the Product List

Start the implementation by adding a new parameter, category, to ProductsController’s List() 

public ViewResult List(string category,[DefaultValue(1)] int page)

and  add a new string property called CurrentCategory to ProductsListViewModel:

namespace SportsStore.WebUI.Models
{
    public class ProductsListViewModel
    {
        public IList<Product> Products { get; set; }
        public PagingInfo PagingInfo { get; set; }
        public string CurrentCategory { get; set; }
    }
}

§5.1.2  Implementing the Category Filter

To implement the filtering behavior, update ProductsController’s List() method as follows:

        public ViewResult List(string category,[DefaultValue(1)] int page)
        {
            var productsToShow = (category == null)?
                productsRepository.Products:
                productsRepository.Products.Where(x => x.Category == category);
            var viewModel = new ProductsListViewModel()
            {
                Products = productsToShow.Skip((page - 1) * PageSize).Take(PageSize).ToList(),
                PagingInfo = new PagingInfo
                {
                    CurrentPage = page,
                    ItemsPerPage = PageSize,
                    TotalItems = productsToShow.Count()
                },
                CurrentCategory=category
            };
            return View(viewModel);
        }

Also, we need to update the global.aspx file because of the routing.

        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.MapRoute(
                null, // Route name
                "{category}// URL with parameters
                new { controller = "Products", action = "List"} // Parameter defaults
            );
        }

in this way, run the application as follows:

1

Maybe you noticed that this title is different .yeah, let’s change it in the Site.master:

        <div id="header">
            <div class="title">
            <asp:ContentPlaceHolder ID="TitleContent" runat="server"/>
            </div>
        </div>

and List.aspx with:

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
	SportsStore :<%=Model.CurrentCategory??"All products" %>
</asp:Content>

 

§5.1.3  Defining a URL Schema for Categories

2

Implement the desired URL schema by replacing your existing RegisterRoutes() method (in Global.asax.cs) with the following:

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

            routes.MapRoute(null,
                "", // Only matches the empty URL (i.e. ~/)
                new{controller = "Products", action = "List", category = (string)null, page = 1 });

            routes.MapRoute(null,
                "Page{page}", // Matches ~/Page2, ~/Page123, but not ~/PageXYZ
                new { controller = "Products", action = "List", category = (string)null },
                new { page = @"\d+" } // Constraints: page must be numerical
            );

            routes.MapRoute(
                null, // Route name
                "{category}", // URL with parameters
                new { controller = "Products", action = "List",page=1} // Parameter defaults
            );

            routes.MapRoute(null,
                "{category}/Page{page}",
                new { controller = "Products", action = "List" }, // Defaults
                new { page = @"\d+" } // Constraints: page must be numerical
                );

            routes.MapRoute(null, "{controller}/{action}");
        }

The golden rule is to put more-specific routes first, so that they’re always chosen in preference to less-specific ones.

And we need to update List.aspx’s call to Html.PageLinks():

    <div class="pager">
        <%= Html.PageLinks(Model.PagingInfo,
            x => Url.Action("List", new {page = x, catetory=Model.CurrentCategory})) %>
    </div>
Now that you’ve done all this, you’ll find that if you visit a URL such as /Chess

 

§5.1.4  Building a Category Navigation Menu

Building a Category Navigation Menu

Get started by creating a new controller class, NavController, I will fullfill thecode later.

Selecting and Rendering a List of Category Links

First, let’s create a new class to describe a link that could be rendered in the navigation menu. Add the following to your Models folder:

namespace SportsStore.WebUI.Models
{
    public class NavLink
    {
        public string Text { get; set; }
        public RouteValueDictionary RouteValues { get; set; }
        public bool IsSelected { get; set; }
    }
}
And update the NavController so that it produces an appropriate list of category data
namespace SportsStore.WebUI.Controllers
{
    public class NavController : Controller
    {
        private IProductsRepository productsRepository;
        public NavController(IProductsRepository productsRepository)
        {
            this.productsRepository = productsRepository;
        }
        public ViewResult menu(string category)
        {
            Func<string, NavLink> makeLink = categoryName => new NavLink
            {
                Text = categoryName ?? "Home",
                RouteValues = new System.Web.Routing.RouteValueDictionary(new
                {
                    controller = "Products",
                    action = "List",
                    category = categoryName==null?null:categoryName.Trim(),
                    page = 1
                }),
                IsSelected=(categoryName==category)
            };
            List<NavLink> navLinks = new List<NavLink>();
            navLinks.Add(makeLink(null));
            // Add a link for each distinct category
            var categories = productsRepository.Products.Select(x => x.Category);
            foreach (string categoryName in categories.Distinct().OrderBy(x => x))
                navLinks.Add(makeLink(categoryName));

            return View(navLinks);
        }
    }
}

Of course , after the controller. We need a view

Since this navigation widget is supposed to be just a fragment of a page, not an entire page in its own right, it makes sense for its view to be a partial view rather than regular view.

Previously you’ve only rendered partial views by calling Html.RenderPartial(), but as you’ll see, it’s just as easy to tell any action method to render a partial view. This is mainly beneficial if you’re using Html.RenderAction() or if you’re using Ajax (see Chapter 14).

3

to make a nice view , we also need to adding a few CSS rules to /Content/Site.css

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; }

4

原文地址:https://www.cnblogs.com/TivonStone/p/1854317.html