目录
介绍
使用代码
过滤图书对象
改进URL方案
创建导航视图组件
创建genres导航
指示当前genres
修复分页
兴趣点
介绍在第1部分中,BooksStore应用程序可以在单个页面上显示数据库中的书籍,在第2部分中,它可以在一个页面上显示较少数量的书籍,并且用户可以从一个页面移动到另一个页面以查看整个目录。在本文中,我们将添加对按类型浏览书籍的支持。
使用代码 过滤图书对象首先,要按Book类型过滤对象,我们将通过更改Models/ViewModels文件夹中的BooksListViewModel.cs文件来添加一个名为CurrentGenre的视图模型属性:
using System.Collections.Generic;
namespace BooksStore.Models.ViewModels
{
public class BooksListViewModel
{
public IEnumerable Books { get; set; }
public PagingInfo PagingInfo { get; set; }
public string CurrentGenre { get; set; }
}
}
下一步是更新Home控制器,以便Index操作方法按genres过滤Book对象,并使用我们添加到视图模型的属性来指示使用以下代码选择了哪个genres:
public IActionResult Index(string genre, int bookPage = 1)
=> View(new BooksListViewModel
{
Books = repository.Books
.Where(p => genre == null || p.Genre == genre)
.OrderBy(p => p.BookID)
.Skip((bookPage - 1) * PageSize)
.Take(PageSize),
PagingInfo = new PagingInfo
{
CurrentPage = bookPage,
ItemsPerPage = PageSize,
TotalItems = repository.Books.Count()
},
CurrentGenre = genre
});
在前面的代码中:
- 我们添加了一个名为genre的参数。此参数用于增强LINQ查询:如果类型不是null,则仅选择具有匹配Genre属性的Book对象。
- 我们还设置了CurrentGenre属性的值。
运行应用程序:
使用以下URL选择自助类型:http://localhost:44333/?genre=Self-Help
但是,这些更改意味着PagingInfo.TotalItems的值计算不正确,因为它没有考虑genres过滤器。而且,很明显,我们和我们的用户不希望使用URL导航到genres。
改进URL方案我们将通过更改Startup类Configure方法中的路由配置来改进URL方案,以使用以下代码创建一组更有用的URL:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute("genpage",
"{genre}/{bookPage:int}",
new { Controller = "Home", action = "Index" });
endpoints.MapControllerRoute("page", "{bookPage:int}",
new { Controller = "Home", action = "Index", bookPage = 1 });
endpoints.MapControllerRoute("genre", "{genre}",
new { Controller = "Home", action = "Index", bookPage = 1 });
endpoints.MapControllerRoute("pagination",
"Books/{bookPage}",
new { Controller = "Home", action = "Index", bookPage = 1 });
endpoints.MapDefaultControllerRoute();
});
通过使用ASP.NET Core路由系统来处理传入请求和生成传出URL,我们可以确保应用程序中的所有URL都是一致的。
现在我们需要一种方法来从视图中接收额外的信息,而不必向标签助手类添加额外的属性。幸运的是,标签助手有一个很好的特性,它允许在一个集合中一起接收具有公共前缀的属性。BooksStore /MyTagHelper文件夹中的MyPageLink.cs文件中的前缀值,代码如下:
public class MyPageLink : TagHelper
{
private IUrlHelperFactory urlHelperFactory;
public MyPageLink(IUrlHelperFactory helperFactory)
{
urlHelperFactory = helperFactory;
}
[ViewContext]
[HtmlAttributeNotBound]
public ViewContext ViewContext { get; set; }
public PagingInfo PageModel { get; set; }
public string PageAction { get; set; }
[HtmlAttributeName(DictionaryAttributePrefix = "page-url-")]
public Dictionary PageUrlValues { get; set; }
= new Dictionary();
public bool PageClassesEnabled { get; set; } = false;
public string PageClass { get; set; }
public string PageClassNormal { get; set; }
public string PageClassSelected { get; set; }
public override void Process(TagHelperContext context,
TagHelperOutput output)
{
IUrlHelper urlHelper = urlHelperFactory.GetUrlHelper(ViewContext);
TagBuilder result = new TagBuilder("div");
for (int i = 1; i x.Genre)
.Distinct()
.OrderBy(x => x));
}
}
}
在前面的代码中,构造函数定义了一个IBooksStoreRepository参数。在该Invoke方法中,我们使用LINQ选择和排序存储库中的genres集,并将它们作为参数传递给该View方法,该方法呈现默认的Razor局部视图,其详细信息从使用IViewComponentResult对象的方法返回。
Razor使用不同的约定来定位视图组件选择的视图。视图的默认名称和搜索视图的位置都与用于控制器的不同。为此,我们将在BooksStore项目中创建Views/Shared/Components/GenreNavigation文件夹,并向其中添加一个名为Default.cshtml的Razor视图,我在其中添加了具有以下标记的内容:
@model IEnumerable
Home
@foreach (string genre in Model)
{
@genre
}
运行应用程序以查看genres导航按钮。如果单击按钮,项目列表将更新为仅显示所选genres的项目,如下图所示:
我们需要向用户提供一些清晰的视觉反馈,以表明选择了哪种类型。为此,第一步,我们将使用该RouteData属性访问请求数据,以获取当前所选genres的值。在下面的代码中,我们将在BooksStore/ViewComponents文件夹中的GenreNavigation.cs文件中传递选定的genres。
public IViewComponentResult Invoke()
{
ViewBag.SelectedGenre = RouteData?.Values["genre"];
return View(repository.Books
.Select(x => x.Genre)
.Distinct()
.OrderBy(x => x));
}
在Invoke方法内部,我们为ViewBag对象动态分配了一个SelectedGenre属性,并将其值设置为当前genres,该genres是通过该RouteData属性返回的上下文对象获取的。ViewBag是一个动态对象,它允许我们简单地通过为它们分配值来定义新属性。
接下来,我们可以更新视图组件选择的视图,并改变用于样式链接的CSS类,以便代表当前类型的类是不同的。为此,我们将使用以下标记更改Views/Shared/Components/GenreNavigation文件夹中的Default.cshtml文件:
@model IEnumerable
Home
@foreach (string genre in Model)
{
@genre
}
我们在class属性中使用了Razor表达式,将btn-primary类应用于表示所选genres的元素,否则应用到btn-outline-primary类。运行应用程序并请求自助类型:
目前,页面链接的数量取决于存储库中的书籍总数,而不是所选类型的书籍数量。这意味着我们可以点击Self-Help类型的第2页的链接,因为没有足够的书来填满两页,所以最终会得到一个空白页,如下图所示:
我们可以通过更新Home控制器中的Index操作方法来解决这个问题,以便分页信息使用以下代码传递genres:
public IActionResult Index(string genre, int bookPage = 1)
=> View(new BooksListViewModel
{
Books = repository.Books
.Where(p => genre == null || p.Genre == genre)
.OrderBy(p => p.BookID)
.Skip((bookPage - 1) * PageSize)
.Take(PageSize),
PagingInfo = new PagingInfo
{
CurrentPage = bookPage,
ItemsPerPage = PageSize,
TotalItems = genre == null ?
repository.Books.Count() :
repository.Books.Where(e =>
e.Genre == genre).Count()
},
CurrentGenre = genre
});
运行应用程序:
我们添加了对分页的支持,以便视图在一个页面上显示较少数量的书籍,并且用户可以从一个页面移动到另一个页面以查看整个目录。我们使用Bootstrap来设计应用程序的外观,我们还添加了对按类型导航书籍的支持。在下一篇文章中,我们将添加一个购物车,它是电子商务应用程序的重要组件。
https://www.codeproject.com/Articles/5327794/An-Introduction-to-ASP-NET-Core-MVC-through-an-E-3