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