ASP.NET Core应用程序7:使用视图组件
视图组件是类,为支持分部视图或者在父视图中注入少量Html或Json数据提供了应用程序逻辑。
1 准备工作
Models文件夹中添加City.cs类和CitiesData类,为CitiesData添加服务。
public class City { public string Name { get; set; } public string Country { get; set; } public int Population { get; set; } }
public class CitiesData { private List<City> cities = new List<City> { new City { Name = "London", Country = "UK", Population = 8539000}, new City { Name = "New York", Country = "USA", Population = 8406000 }, new City { Name = "San Jose", Country = "USA", Population = 998537 }, new City { Name = "Paris", Country = "France", Population = 2244000 } }; public IEnumerable<City> Cities => cities; public void AddCity(City newCity) { cities.Add(newCity); } }
services.AddSingleton<CitiesData>();
在Pages文件夹下添加Cities.cshtml的Razor Pages。
@page @inject CitiesData Data <div class="m-2"> <table class="table table-sm table-striped table-bordered"> <tbody> @foreach (var item in Data.Cities) { <tr> <td>@item.Name</td> <td>@item.Country</td> <td>@item.Population</td> </tr> } </tbody> </table> </div>
浏览器请求http://localhost:5000/cities。
2 理解视图组件
可将视图组件想象成一个专门的操作页面,但它只用于为分部视图提供数据,不能接收http请求,而且提供的内容将总是被包含到父视图中。
3 创建和使用视图组件
视图组件是名称以ViewComponent结束,并定义了Invoke或InvokeAsync方法的类,派生自ViewComponent基类,或者使用ViewComponent特性装饰的类。
在项目的任何位置都可以定义视图组件,但约定做法是把它们放到名为Components的文件夹中。
创建Components文件夹,添加CitySummary.cs。
public class CitySummary : ViewComponent { private CitiesData data; public CitySummary(CitiesData cdata) { data = cdata; } public string Invoke() { return $"{data.Cities.Count()}座城市," + $"{data.Cities.Sum(a => a.Population)}人"; } }
(1)应用视图组件
使用添加到视图和Pazor Pages生成的C#类的Component属性,该属性返回的对象实现了IViewComponentHelper接口,接口提供了InvokeAsync方法。
如下Views/Home/Index.cshtml文件中应用视图组件,在使用时将视图组件类名作为实参。把名称空间添加到Views/_ViewImports.cshtml中。
@section Summary { <div class="bg-info text-white m-2 p-2"> @await Component.InvokeAsync("CitySummary") </div> }
@using MyWebApp.Components
浏览器请求http://localhost:5000/home/index/1。
(2)使用标签助手应用视图组件
在Views/_ViewImports.cshtml中添加配置标签助手。修改Views/Home/Index.cshtml文件中应用视图组件。
@addTagHelper *,MyWebApp
@section Summary { <div class="bg-info text-white m-2 p-2" <vc:city-summary /> </div> }
4 理解视图组件结果
视图组件通过让Invoke方法返回一个实现了IViewComponentResult接口的对象,可以返回更复杂的效果。3个内置类实现了此接口,这些类以及ViewComponent基类提供了创建的方法。
如下三个内置类:
- ViewViewComponentResult:用于手动指定一个Razor视图,包括可选视图模型数据。使用View方法可创建其实例。
- ContentViewComponentResult:用于指定文本结果,该文本结果将被安全编码。使用Content方法可创建其实例。
- HtmlContentViewComponentResult:用于指定一个Html片段,该片段不会被编码,直接包含到html中。没有ViewComponent方法可创建其实例。
4.1 返回一个分部视图
最有用的响应是ViewViewComponentResult,它告诉Razor渲染一个分部视图,并在父视图中包含渲染结果。ViewComponent基类提供了View方法用于创建ViewViewComponentResult对像。
在Models文件夹下添加CityViewModel.cs视图模型类。修改CitySummary视图组件的Invoke方法。
public class CityViewModel { public int Cities { get; set; } public int Population { get; set; } }
public IViewComponentResult Invoke() { var model = new CityViewModel() { Cities = data.Cities.Count(), Population = data.Cities.Sum(x => x.Population), }; return View(model); }
使用浏览器请求http://localhost:5000/home/index/1,因为还没有可供视图组件使用的视图所以会报错,但可以看出使用该视图组件时都搜索了哪些位置。
InvalidOperationException: The view 'Components/CitySummary/Default' was not found. The following locations were searched: /Views/Home/Components/CitySummary/Default.cshtml /Views/Shared/Components/CitySummary/Default.cshtml /Pages/Shared/Components/CitySummary/Default.cshtml
创建Views/Shared/Components/CitySummary文件夹,添加Default.cshtml的Razor视图。
@model CityViewModel <table class="table table-sm table-bordered text-white bg-@ViewBag.Theme"> <thead> <tr><th colspan="2">Cities Summary</th></tr> </thead> <tbody> <tr> <td>Cities:</td> <td class="text-right"> @Model.Cities </td> </tr> <tr> <td>Population:</td> <td class="text-right"> @Model.Population.ToString("#,###") </td> </tr> </tbody> </table>
使用浏览器请求http://localhost:5000/home/index/1和http://localhost:5000/data。
4.2 返回Html片段
ContentViewComponentResult类用于不使用视图情况下,在父视图中包含Html片段。使用从ViewComponent基类继承的Content方法,可以创建其实例。
修改如下。
public IViewComponentResult Invoke() { return Content("这是一个<h3><i>string</i></h3>"); }
使用浏览器请求http://localhost:5000/data,会看到返回的结果并没有解释为html元素,Content方法是编码的。要想把<h3><i>标签转换成html显示,则必须创建HtmlContentViewComponentResult对象,为其构造函数提供一个HtmlString对象。
using Microsoft.AspNetCore.Mvc.ViewComponents; using Microsoft.AspNetCore.Html;
public IViewComponentResult Invoke() { var htmlString = new HtmlString("这是一个<h3><i>string</i></h3>"); return new HtmlContentViewComponentResult(htmlString); }
5 获取上下文数据
通过ViewComponent基类定义的一些属性,将关于当前请求和父视图的详细信息提供给视图组件。
在Component/CitySummary.cs中使用请求数据。
public string Invoke() { if (RouteData.Values["controller"] != null) { return "controller请求"; } else { return "Razor Pages请求"; } }
5.1 使用实参提供父视图的上下文
父视图能够为视图组件提供额外的上下文数据,包括应该生成的内容的数据。
public IViewComponentResult Invoke(string themeName) { ViewBag.Theme = themeName; return View(new CityViewModel { Cities = data.Cities.Count(), Population = data.Cities.Sum(c => c.Population) }); }
在Views/Shared/Components/CitySummar/Default.cshtml文件中设置内容样式。
<table class="table table-sm table-bordered text-white bg-@ViewBag.Theme"> ......
这时必须为视图组件的Invoke方法提供参数值。修改Views/Home/Index.cshtml。
<vc:city-summary theme-name="secondary" />
修改Pages/Data.cshtml。
<vc:city-summary theme-name="danger" />
也可以使用组件助手提供值。
@await Component.InvokeAsync("CitySummary",new { themmeName = "danger"})
5.2 创建异步视图组件
可通过定义一个返回Task的InvokeAsync方法,创建异步视图组件。
在Components文件夹下添加PageSize.cs的类文件。
public class PageSize : ViewComponent { public async Task<IViewComponentResult> InvokeAsync() { HttpClient client = new HttpClient(); HttpResponseMessage response = await client.GetAsync("http://apress.com"); return View(response.Content.Headers.ContentLength); } }
创建Views/Shared/Components/文件夹,添加Default.cshtml。
@model long <div class="m-1 p-1 bg-light text-dark">Page size: @Model</div>
在Views/Home/Index.cshtml中使用异步组件。
<vc:page-size />
6 创建视图组件类
视图组件是提供功能的一个汇总或快照,由控制器或Razor Pages来深入处理功能。
在Pages文件夹Cities.cshtml.cs中添加如下。
[ViewComponent(Name = "CitiesPageHybrid")] public class CitiesModel : PageModel { public CitiesModel(CitiesData cdata) { Data = cdata; } public CitiesData Data { get; set; } [ViewComponentContext] public ViewComponentContext Context { get; set; } public IViewComponentResult Invoke() { return new ViewViewComponentResult() { ViewData = new ViewDataDictionary<CityViewModel>( Context.ViewData, new CityViewModel { Cities = Data.Cities.Count(), Population = Data.Cities.Sum(c => c.Population) }) }; } }
这个页面模型使用了ViewComponent从而允许它用作一个视图组件,Name参数制定了名称。因为此类不能再继承ViewComponent基类,所以使用ViewComponentContext特性来装饰ViewComponentContext属性,这表明在调用Invoke方法之前,应该把值赋给Context.ViewData,View方法不可用,所以创建了ViewViewComponentResult,它依赖于通过被装饰的属性收到上下文对象。
在Cities.cshtml中更新视图。
@page @model MyWebApp.Pages.CitiesModel <div class="m-2"> <table class="table table-sm table-striped table-bordered"> <tbody> @foreach (var item in Model.Data.Cities) { <tr> <td>@item.Name</td> <td>@item.Country</td> <td>@item.Population</td> </tr> } </tbody> </table> </div>
为创建混合视图组件的视图,需创建Pages/Shared/Components/CitiesPageHybrid文件夹,添加Default.cshtml。
@model CityViewModel <table class="table table-sm table-bordered text-white bg-dark"> <thead><tr><th colspan="2">Hybrid Page Summary</th></tr></thead> <tbody> <tr> <td>Cities:</td> <td class="text-right">@Model.Cities</td> </tr> <tr> <td>Population:</td> <td class="text-right"> @Model.Population.ToString("#,###") </td> </tr> </tbody> </table>
在Pages/Data.cshtml中使用组件。
<div class="bg-info text-white m-2 p-2"> <vc:cities-page-hybrid /> </div>
请求http://localhost:5000/cities和http://localhost:5000/data。
本文来自博客园,作者:一纸年华,转载请注明原文链接:https://www.cnblogs.com/nullcodeworld/p/18200184
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!