ASP.NET Core 中的视图组件
[删除(380066935@qq.com或微信通知)]
更好的阅读体验请查看原文:https://docs.microsoft.com/zh-cn/aspnet/core/mvc/views/view-components?view=aspnetcore-6.0
视图组件
视图组件与分部视图类似,但它们的功能更加强大。 视图组件不使用模型绑定,具体取决于调用视图组件时传递的数据。 本文是使用控制器和视图编写的,但视图组件适用于 Razor Pages。
视图组件:
- 呈现一个区块而不是整个响应。
- 包括控制器和视图间发现的相同关注点分离和可测试性优势。
- 可以有参数和业务逻辑。
- 通常从布局页调用。
视图组件适用于任何可重用呈现逻辑,对于部分视图来说太复杂,例如:
- 动态导航菜单
- 标记云,在其中查询数据库
- 登录面板
- 购物车
- 最近发布的文章
- 博客上的边栏内容
- 将在每一页上呈现的登录面板,并显示注销或登录的链接,具体取决于用户的登录状态
视图组件由两个部分组成:
- 类,通常派生自 ViewComponent
- 它返回的结果,通常是一个视图。
与控制器一样,视图组件可以是 POCO,但大多数开发人员都可以利用派生自 ViewComponent的方法和属性。
在考虑视图组件是否符合应用的规范时,请考虑改用 Razor 组件。 Razor 组件还将标记与 C# 代码组合来生成可重用的 UI 单元。 Razor 组件专用于让开发人员在提供客户端 UI 逻辑和组合时保持高效。 有关详细信息,请参阅 ASP.NET Core 组件。 有关如何将组件合并Razor到 MVC 或 Razor Pages 应用中的信息,请参阅预呈现和集成 ASP.NET CoreRazor组件。
创建视图组件
本部分包含创建视图组件的高级别要求。 本文后续部分将详细检查每个步骤并创建视图组件。
视图组件类
可通过以下任一方法创建视图组件类:
- 源于 ViewComponent
- 使用
[ViewComponent]
属性修饰类,或者从具有[ViewComponent]
属性的类派生 - 创建名称以后缀结尾的类
ViewComponent
与控制器一样,视图组件必须是公共、非嵌套和非抽象的类。 视图组件名称是删除后缀的 ViewComponent
类名。 也可以使用 Name 属性显式指定它。
视图组件类:
若要防止具有不区分 ViewComponent
大小写的后缀的类被视为视图组件,请使用 [NonViewComponent]
属性修饰类:
using Microsoft.AspNetCore.Mvc;
[NonViewComponent]
public class ReviewComponent
{
public string Status(string name) => JobStatus.GetCurrentStatus(name);
}
视图组件方法
视图组件在以下项中定义其逻辑:
InvokeAsync
返回Task<IViewComponentResult>
的方法 。Invoke
返回 . IViewComponentResult.
参数直接来自视图组件的调用,而不是来自模型绑定。 视图组件从不直接处理请求。 通常,视图组件通过调用 View
方法来初始化模型并将其传递到视图。 总之,视图组件方法:
- 定义返回
Task<IViewComponentResult>
的InvokeAsync
方法,或是返回IViewComponentResult
的同步Invoke
方法。 - 通常通过调用 ViewComponent.View 方法初始化模型并将其传递给视图。
- 参数来自调用方法,而不是 HTTP。 没有模型绑定。
- 无法直接作为 HTTP 终结点访问。 它们通常在视图中调用。 视图组件从不处理请求。
- 在签名上重载,而不是当前 HTTP 请求的任何详细信息。
视图搜索路径
运行时在以下路径中搜索视图:
- /Views/{Controller Name}/Components/{View Component Name}/{View Name}
- /Views/Shared/Components/{View Component Name}/{View Name}
- /Pages/Shared/Components/{View Component Name}/{View Name}
搜索路径适用于使用控制器 + 视图和 Razor Pages 的项目。
视图组件的默认视图名称是 Default
,这意味着通常将命名 Default.cshtml
视图文件。 创建视图组件结果或调用 View
方法时,可以指定其他视图名称。
建议命名视图文件 Default.cshtml
,并使用 Views/Shared/Components/{View Component Name}/{View Name} 路径。 PriorityList
此示例Views/Shared/Components/PriorityList/Default.cshtml
中使用的视图组件用于视图组件视图。
自定义视图搜索路径
修改 Razor 的 ViewLocationFormats 集合,以自定义视图搜索路径。 例如,若要在路径 /Components/{View Component Name}/{View Name}
中搜索视图,请将新项添加到集合中:
using Microsoft.EntityFrameworkCore;
using ViewComponentSample.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews()
.AddRazorOptions(options =>
{
options.ViewLocationFormats.Add("/{0}.cshtml");
});
builder.Services.AddDbContext<ToDoContext>(options =>
options.UseInMemoryDatabase("db"));
var app = builder.Build();
// Remaining code removed for brevity.
在前面的代码中,占位符 {0}
表示路径 Components/{View Component Name}/{View Name}
。
调用视图组件
要使用视图组件,请在视图中调用以下内容:
@await Component.InvokeAsync("Name of view component",
{Anonymous Type Containing Parameters})
参数将 InvokeAsync
传递给方法。 PriorityList
从Views/ToDo/Index.cshtml
视图文件调用本文中开发的视图组件。 在以下代码中,使用 InvokeAsync
两个参数调用该方法:
</table>
<div>
Maxium Priority: @ViewData["maxPriority"] <br />
Is Complete: @ViewData["isDone"]
@await Component.InvokeAsync("PriorityList",
new {
maxPriority = ViewData["maxPriority"],
isDone = ViewData["isDone"] }
)
</div>
将视图组件作为标记帮助程序调用
可以将视图组件作为 标记帮助程序调用:
<div>
Maxium Priority: @ViewData["maxPriority"] <br />
Is Complete: @ViewData["isDone"]
@{
int maxPriority = Convert.ToInt32(ViewData["maxPriority"]);
bool isDone = Convert.ToBoolean(ViewData["isDone"]);
}
<vc:priority-list max-priority=maxPriority is-done=isDone>
</vc:priority-list>
</div>
标记帮助程序采用 Pascal 大小写格式的类和方法参数将转换为各自相应的短横线格式。 要调用视图组件的标记帮助程序使用 <vc></vc>
元素。 按如下方式指定视图组件:
<vc:[view-component-name]
parameter1="parameter1 value"
parameter2="parameter2 value">
</vc:[view-component-name]>
若要将视图组件用作标记帮助程序,请使用 @addTagHelper
指令注册包含视图组件的程序集。 如果视图组件位于名为 MyWebApp
的程序集中,请将 _ViewImports.cshtml
以下指令添加到文件中:
@addTagHelper *, MyWebApp
可以将视图组件注册为标记帮助程序,以引用视图组件的任何文件。 要详细了解如何注册标记帮助程序,请参阅管理标记帮助程序作用域。
本教程中使用的 InvokeAsync
方法:
@await Component.InvokeAsync("PriorityList",
new {
maxPriority = ViewData["maxPriority"],
isDone = ViewData["isDone"] }
)
在前面的标记中, PriorityList
视图组件变为 priority-list
。 视图组件的参数作为短横线格式的属性进行传递。
直接从控制器调用视图组件
视图组件通常从视图中调用,但可以直接从控制器方法调用它们。 虽然视图组件不定义控制器等终结点,但可以实现返回内容的 ViewComponentResult
控制器操作。
在以下示例中,视图组件直接从控制器调用:
public IActionResult IndexVC(int maxPriority = 2, bool isDone = false)
{
return ViewComponent("PriorityList",
new {
maxPriority = maxPriority,
isDone = isDone
});
}
创建基本视图组件
下载、生成和测试起始代码。 它是一个包含 ToDo
控制器的基本项目,它显示 ToDo 项的列表。
更新控制器以传入优先级和完成状态
更新方法 Index
以使用优先级和完成状态参数:
using Microsoft.AspNetCore.Mvc;
using ViewComponentSample.Models;
namespace ViewComponentSample.Controllers;
public class ToDoController : Controller
{
private readonly ToDoContext _ToDoContext;
public ToDoController(ToDoContext context)
{
_ToDoContext = context;
_ToDoContext.Database.EnsureCreated();
}
public IActionResult Index(int maxPriority = 2, bool isDone = false)
{
var model = _ToDoContext!.ToDo!.ToList();
ViewData["maxPriority"] = maxPriority;
ViewData["isDone"] = isDone;
return View(model);
}
添加 ViewComponent 类
将 ViewComponent 类添加到 ViewComponents/PriorityListViewComponent.cs
:
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using ViewComponentSample.Models;
namespace ViewComponentSample.ViewComponents;
public class PriorityListViewComponent : ViewComponent
{
private readonly ToDoContext db;
public PriorityListViewComponent(ToDoContext context) => db = context;
public async Task<IViewComponentResult> InvokeAsync(
int maxPriority, bool isDone)
{
var items = await GetItemsAsync(maxPriority, isDone);
return View(items);
}
private Task<List<TodoItem>> GetItemsAsync(int maxPriority, bool isDone)
{
return db!.ToDo!.Where(x => x.IsDone == isDone &&
x.Priority <= maxPriority).ToListAsync();
}
}
代码说明:
视图组件类可以包含在项目的任意文件夹中。
由于类名 PriorityListViewComponent 以后缀 ViewComponent 结尾,因此运行时在从视图中引用类组件时使用字符串
PriorityList
。[ViewComponent]
属性可以更改用于引用视图组件的名称。 例如,类可能已使用以下[ViewComponent]
属性进行命名XYZ
:[ViewComponent(Name = "PriorityList")] public class XYZ : ViewComponent
[ViewComponent]
上述代码中的属性指示视图组件选择器使用:- 查找与组件关联的视图时的名称
PriorityList
- 从视图中引用类组件时,字符串“PriorityList”。
- 查找与组件关联的视图时的名称
组件使用依赖关系注入以使数据上下文可用。
InvokeAsync
公开可从视图中调用的方法,并且可以采用任意数量的参数。InvokeAsync
方法返回满足isDone
和maxPriority
参数的ToDo
项集。
创建视图组件 Razor 视图
创建 Views/Shared/Components 文件夹。 此文件夹 必须 命名为 Components。
创建 Views/Shared/Components/PriorityList 文件夹。 此文件夹名称必须与视图组件类的名称匹配,或类的名称减去后缀。 如果使用属性
ViewComponent
,则类名需要与属性指定匹配。创建
Views/Shared/Components/PriorityList/Default.cshtml
Razor 视图:@model IEnumerable<ViewComponentSample.Models.TodoItem> <h3>Priority Items</h3> <ul> @foreach (var todo in Model) { <li>@todo.Name</li> } </ul>
Razor 视图获取并显示
TodoItem
列表。 如果视图组件InvokeAsync
方法未传递视图的名称, 则默认 按约定用于视图名称。 要替代特定控制器的默认样式,请将视图添加到控制器特定的视图文件夹(例如 Views/ToDo/Components/PriorityList/Default.cshtml)。如果视图组件特定于控制器,则可以将其添加到特定于控制器的文件夹。 例如,
Views/ToDo/Components/PriorityList/Default.cshtml
特定于控制器。div
将包含对优先级列表组件的调用添加到文件底部Views/ToDo/index.cshtml
:</table> <div> Maxium Priority: @ViewData["maxPriority"] <br /> Is Complete: @ViewData["isDone"] @await Component.InvokeAsync("PriorityList", new { maxPriority = ViewData["maxPriority"], isDone = ViewData["isDone"] } ) </div>
标记 @await Component.InvokeAsync
显示调用视图组件的语法。 第一个参数是要调用的组件的名称。 后续参数将传递给该组件。 InvokeAsync
可以采用任意数量的参数。
测试应用。 下图显示 ToDo 列表和优先级项:
可以直接从控制器调用视图组件:
public IActionResult IndexVC(int maxPriority = 2, bool isDone = false)
{
return ViewComponent("PriorityList",
new {
maxPriority = maxPriority,
isDone = isDone
});
}
指定视图组件名称
在某些情况下,复杂的视图组件可能需要指定非默认视图。 以下代码显示如何从 InvokeAsync
方法指定“PVC”视图。 更新 PriorityListViewComponent
类中的 InvokeAsync
方法。
public async Task<IViewComponentResult> InvokeAsync(
int maxPriority, bool isDone)
{
string MyView = "Default";
// If asking for all completed tasks, render with the "PVC" view.
if (maxPriority > 3 && isDone == true)
{
MyView = "PVC";
}
var items = await GetItemsAsync(maxPriority, isDone);
return View(MyView, items);
}
将 Views/Shared/Components/PriorityList/Default.cshtml
文件复制到名为 “ Views/Shared/Components/PriorityList/PVC.cshtml
. 添加标题以指示正在使用 PVC 视图。
@model IEnumerable<ViewComponentSample.Models.TodoItem>
<h2> PVC Named Priority Component View</h2>
<h4>@ViewBag.PriorityMessage</h4>
<ul>
@foreach (var todo in Model)
{
<li>@todo.Name</li>
}
</ul>
运行应用并验证 PVC 视图。
如果未呈现 PVC 视图,请验证优先级为 4 或更高版本的视图组件。
检查视图路径
将优先级参数更改为 3 或更低,从而不返回优先级视图。
暂时重命名为
Views/ToDo/Components/PriorityList/Default.cshtml
1Default.cshtml
.测试应用,会发生以下错误:
An unhandled exception occurred while processing the request. InvalidOperationException: The view 'Components/PriorityList/Default' wasn't found. The following locations were searched: /Views/ToDo/Components/PriorityList/Default.cshtml /Views/Shared/Components/PriorityList/Default.cshtml
将
Views/ToDo/Components/PriorityList/1Default.cshtml
复制到Views/Shared/Components/PriorityList/Default.cshtml
。将一些标记添加到共享 ToDo 视图组件视图,以指示视图来自“Shared”文件夹。
测试“共享”组件视图。
避免硬编码字符串
为了获得编译时间安全性,请将硬编码的视图组件名称替换为类名。 将 PriorityListViewComponent.cs 文件更新为不使用“ViewComponent”后缀:
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using ViewComponentSample.Models;
namespace ViewComponentSample.ViewComponents;
public class PriorityList : ViewComponent
{
private readonly ToDoContext db;
public PriorityList(ToDoContext context)
{
db = context;
}
public async Task<IViewComponentResult> InvokeAsync(
int maxPriority, bool isDone)
{
var items = await GetItemsAsync(maxPriority, isDone);
return View(items);
}
private Task<List<TodoItem>> GetItemsAsync(int maxPriority, bool isDone)
{
return db!.ToDo!.Where(x => x.IsDone == isDone &&
x.Priority <= maxPriority).ToListAsync();
}
}
视图文件:
</table>
<div>
Testing nameof(PriorityList) <br />
Maxium Priority: @ViewData["maxPriority"] <br />
Is Complete: @ViewData["isDone"]
@await Component.InvokeAsync(nameof(PriorityList),
new {
maxPriority = ViewData["maxPriority"],
isDone = ViewData["isDone"] }
)
</div>
采用 CLR 类型的方法重 Component.InvokeAsync
载使用 typeof
运算符:
</table>
<div>
Testing typeof(PriorityList) <br />
Maxium Priority: @ViewData["maxPriority"] <br />
Is Complete: @ViewData["isDone"]
@await Component.InvokeAsync(typeof(PriorityList),
new {
maxPriority = ViewData["maxPriority"],
isDone = ViewData["isDone"] }
)
</div>
执行同步工作
如果不需要异步工作,框架将处理调用同步 Invoke
方法。 以下方法将创建同步 Invoke
视图组件:
using Microsoft.AspNetCore.Mvc;
using ViewComponentSample.Models;
namespace ViewComponentSample.ViewComponents
{
public class PriorityListSync : ViewComponent
{
private readonly ToDoContext db;
public PriorityListSync(ToDoContext context)
{
db = context;
}
public IViewComponentResult Invoke(int maxPriority, bool isDone)
{
var x = db!.ToDo!.Where(x => x.IsDone == isDone &&
x.Priority <= maxPriority).ToList();
return View(x);
}
}
}
视图组件 Razor 的文件:
<div>
Testing nameof(PriorityList) <br />
Maxium Priority: @ViewData["maxPriority"] <br />
Is Complete: @ViewData["isDone"]
@await Component.InvokeAsync(nameof(PriorityListSync),
new {
maxPriority = ViewData["maxPriority"],
isDone = ViewData["isDone"] }
)
</div>
例如,在文件中调用 Razor 视图组件 (, Views/Home/Index.cshtml
) 使用以下方法之一:
若要使用 IViewComponentHelper 方法,请调用 Component.InvokeAsync
:
@await Component.InvokeAsync(nameof(PriorityList),
new { maxPriority = 4, isDone = true })
若要使用标记帮助程序,请使用 @addTagHelper
指令注册包含视图组件的程序集(视图组件位于名为 MyWebApp
的程序集中):
@addTagHelper *, MyWebApp
在 Razor 标记文件中使用视图组件标记帮助程序:
<vc:priority-list max-priority="999" is-done="false">
</vc:priority-list>
PriorityList.Invoke
的方法签名是同步的,但 Razor 在标记文件中使用 Component.InvokeAsync
找到并调用该方法。
其他资源
视图组件
视图组件与分部视图类似,但它们的功能更加强大。 视图组件不使用模型绑定,并且仅依赖调用时提供的数据。 本文是使用控制器和视图编写的,但视图组件也适用于 Razor Pages。
视图组件:
- 呈现一个区块而不是整个响应。
- 包括控制器和视图间发现的相同关注点分离和可测试性优势。
- 可以有参数和业务逻辑。
- 通常从布局页调用。
视图组件可用于具有可重用呈现逻辑(对分部视图来说过于复杂)的任何位置,例如:
- 动态导航菜单
- 标记云(查询数据库的位置)
- 登录面板
- 购物车
- 最近发布的文章
- 典型博客上的边栏内容
- 一个登录面板,呈现在每页上并显示注销或登录链接,具体取决于用户的登录状态
视图组件由两个部分组成:类 (通常派生自 ViewComponent) ,并且它返回的结果通常是视图) (。 与控制器一样,视图组件可以是 POCO,但大多数开发人员都利用派生自 ViewComponent
的方法和属性。
在考虑视图组件是否符合应用的规范时,请考虑改用 Razor 组件。 Razor 组件还将标记与 C# 代码组合来生成可重用的 UI 单元。 Razor 组件专用于让开发人员在提供客户端 UI 逻辑和组合时保持高效。 有关详细信息,请参阅 ASP.NET Core 组件。 有关如何将组件合并Razor到 MVC 或 Razor Pages 应用中的信息,请参阅预呈现和集成 ASP.NET CoreRazor组件。
创建视图组件
本部分包含创建视图组件的高级别要求。 本文后续部分将详细检查每个步骤并创建视图组件。
视图组件类
可通过以下任一方法创建视图组件类:
- 从 ViewComponent 派生
- 使用
[ViewComponent]
属性修饰类,或者从具有[ViewComponent]
属性的类派生 - 创建名称以 ViewComponent 后缀结尾的类
与控制器一样,视图组件必须是公共、非嵌套和非抽象的类。 视图组件名称是删除了“ViewComponent”后缀的类名。 也可以使用 ViewComponentAttribute.Name
属性显式指定它。
视图组件类:
要阻止将具有 ViewComponent 后缀(不区分大小写)的类视为视图组件,请使用 [NonViewComponent] 属性修饰该类:
[NonViewComponent]
public class ReviewComponent
{
// ...
视图组件方法
视图组件以返回 Task<IViewComponentResult>
的 InvokeAsync
方法,或是以返回 IViewComponentResult
的同步 Invoke
方法定义其逻辑。 参数直接来自视图组件的调用,而不是来自模型绑定。 视图组件从不直接处理请求。 通常,视图组件通过调用 View
方法来初始化模型并将其传递到视图。 总之,视图组件方法:
- 定义返回
Task<IViewComponentResult>
的InvokeAsync
方法,或是返回IViewComponentResult
的同步Invoke
方法。 - 一般通过调用
ViewComponent
View
方法来初始化模型并将其传递到视图。 - 参数来自调用方法,而不是 HTTP。 没有模型绑定。
- 不可直接作为 HTTP 终结点进行访问。 通过代码调用它们(通常在视图中)。 视图组件从不处理请求。
- 在签名上重载,而不是当前 HTTP 请求的任何详细信息。
视图搜索路径
运行时在以下路径中搜索视图:
- /Views/{Controller Name}/Components/{View Component Name}/{View Name}
- /Views/Shared/Components/{View Component Name}/{View Name}
- /Pages/Shared/Components/{View Component Name}/{View Name}
搜索路径适用于使用控制器 + 视图和 Razor Pages 的项目。
视图组件的默认视图名称为 Default,这意味着视图文件通常命名 Default.cshtml
。 可以在创建视图组件结果或调用 View
方法时指定不同的视图名称。
建议命名视图文件 Default.cshtml
,并使用 Views/Shared/Components/{View Component Name}/{View Name} 路径。 PriorityList
此示例Views/Shared/Components/PriorityList/Default.cshtml
中使用的视图组件用于视图组件视图。
自定义视图搜索路径
修改 Razor 的 ViewLocationFormats 集合,以自定义视图搜索路径。 例如,将新项添加到集合,以搜索路径“/Components/{视图组件名称}/{视图名称}”中的视图:
services.AddMvc()
.AddRazorOptions(options =>
{
options.ViewLocationFormats.Add("/{0}.cshtml");
})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
在前面的代码中,占位符“{0}”表示路径“Components/{视图组件名称}/{视图名称}”。
调用视图组件
要使用视图组件,请在视图中调用以下内容:
@await Component.InvokeAsync("Name of view component", {Anonymous Type Containing Parameters})
参数将传递给 InvokeAsync
方法。 PriorityList
从Views/ToDo/Index.cshtml
视图文件调用本文中开发的视图组件。 在下例中,使用两个参数调用 InvokeAsync
方法:
@await Component.InvokeAsync("PriorityList", new { maxPriority = 4, isDone = true })
调用视图组件作为标记帮助程序
对于 ASP.NET Core 1.1 及更高版本,可以调用视图组件作为标记帮助程序:
<vc:priority-list max-priority="2" is-done="false">
</vc:priority-list>
标记帮助程序采用 Pascal 大小写格式的类和方法参数将转换为各自相应的短横线格式。 要调用视图组件的标记帮助程序使用 <vc></vc>
元素。 按如下方式指定视图组件:
<vc:[view-component-name]
parameter1="parameter1 value"
parameter2="parameter2 value">
</vc:[view-component-name]>
若要将视图组件用作标记帮助程序,请使用 @addTagHelper
指令注册包含视图组件的程序集。 如果视图组件位于名为 MyWebApp
的程序集中,请将 _ViewImports.cshtml
以下指令添加到文件中:
@addTagHelper *, MyWebApp
可将视图组件作为标记帮助程序注册到任何引用视图组件的文件。 要详细了解如何注册标记帮助程序,请参阅管理标记帮助程序作用域。
本教程中使用的 InvokeAsync
方法:
@await Component.InvokeAsync("PriorityList", new { maxPriority = 4, isDone = true })
在标记帮助程序标记中:
<vc:priority-list max-priority="2" is-done="false">
</vc:priority-list>
在以上示例中,PriorityList
视图组件变为 priority-list
。 视图组件的参数作为短横线格式的属性进行传递。
从控制器直接调用视图组件
视图组件通常从视图调用,但你可以直接从控制器方法调用它们。 尽管视图组件不定义控制器等终结点,但你可以轻松实现返回 ViewComponentResult
内容的控制器操作。
在此示例中,视图组件直接从控制器调用:
public IActionResult IndexVC()
{
return ViewComponent("PriorityList", new { maxPriority = 3, isDone = false });
}
演练:创建简单的视图组件
下载、生成和测试起始代码。 它是一个包含 ToDo
控制器的简单项目,它显示 ToDo 项的列表。
添加 ViewComponent 类
创建一个 ViewComponents 文件夹并添加以下 PriorityListViewComponent
类:
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ViewComponentSample.Models;
namespace ViewComponentSample.ViewComponents
{
public class PriorityListViewComponent : ViewComponent
{
private readonly ToDoContext db;
public PriorityListViewComponent(ToDoContext context)
{
db = context;
}
public async Task<IViewComponentResult> InvokeAsync(
int maxPriority, bool isDone)
{
var items = await GetItemsAsync(maxPriority, isDone);
return View(items);
}
private Task<List<TodoItem>> GetItemsAsync(int maxPriority, bool isDone)
{
return db.ToDo.Where(x => x.IsDone == isDone &&
x.Priority <= maxPriority).ToListAsync();
}
}
}
代码说明:
视图组件类可以包含在项目的任意文件夹中。
由于类名 PriorityListViewComponent 以后缀 ViewComponent 结尾,因此运行时在从视图中引用类组件时使用字符串
PriorityList
。[ViewComponent]
属性可以更改用于引用视图组件的名称。 例如,该类可能已使用ViewComponent
属性进行命名XYZ
:[ViewComponent(Name = "PriorityList")] public class XYZ : ViewComponent
[ViewComponent]
上述代码中的属性告知视图组件选择器使用:- 查找与组件关联的视图时的名称
PriorityList
- 从视图中引用类组件时,字符串“PriorityList”。
- 查找与组件关联的视图时的名称
组件使用依赖关系注入以使数据上下文可用。
InvokeAsync
公开可以从视图调用的方法,且可以采用任意数量的参数。InvokeAsync
方法返回满足isDone
和maxPriority
参数的ToDo
项集。
创建视图组件 Razor 视图
创建 Views/Shared/Components 文件夹。 此文件夹 必须 命名
Components
。创建 Views/Shared/Components/PriorityList 文件夹。 此文件夹名称必须与视图组件类的名称或类名去掉后缀(如果遵照约定并在类名中使用了“ViewComponent”后缀)的名称相匹配。 如果使用了
ViewComponent
属性,则类名称需要匹配指定的属性。创建
Views/Shared/Components/PriorityList/Default.cshtml
Razor 视图:@model IEnumerable<ViewComponentSample.Models.TodoItem> <h3>Priority Items</h3> <ul> @foreach (var todo in Model) { <li>@todo.Name</li> } </ul>
Razor 视图获取并显示
TodoItem
列表。 如果视图组件InvokeAsync
方法未按示例) 传递视图的名称 (, 则 默认按约定用于视图名称。 在本教程后面部分,我将演示如何传递视图名称。 要替代特定控制器的默认样式,请将视图添加到控制器特定的视图文件夹(例如 Views/ToDo/Components/PriorityList/Default.cshtml)。如果视图组件特定于控制器,则可以将其添加到特定于控制器的文件夹 (
Views/ToDo/Components/PriorityList/Default.cshtml
) 。将
div
包含对优先级列表组件的调用添加到文件底部Views/ToDo/index.cshtml
:</table> <div> @await Component.InvokeAsync("PriorityList", new { maxPriority = 2, isDone = false }) </div>
标记 @await Component.InvokeAsync
显示调用视图组件的语法。 第一个参数是要调用的组件的名称。 后续参数将传递给该组件。 InvokeAsync
可以采用任意数量的参数。
测试应用。 下图显示 ToDo 列表和优先级项:
也可直接从控制器调用视图组件:
public IActionResult IndexVC()
{
return ViewComponent("PriorityList", new { maxPriority = 3, isDone = false });
}
指定视图名称
在某些情况下,复杂的视图组件可能需要指定非默认视图。 以下代码显示如何从 InvokeAsync
方法指定“PVC”视图。 更新 PriorityListViewComponent
类中的 InvokeAsync
方法。
public async Task<IViewComponentResult> InvokeAsync(
int maxPriority, bool isDone)
{
string MyView = "Default";
// If asking for all completed tasks, render with the "PVC" view.
if (maxPriority > 3 && isDone == true)
{
MyView = "PVC";
}
var items = await GetItemsAsync(maxPriority, isDone);
return View(MyView, items);
}
将 Views/Shared/Components/PriorityList/Default.cshtml
文件复制到名为 “ Views/Shared/Components/PriorityList/PVC.cshtml
. 添加标题以指示正在使用 PVC 视图。
@model IEnumerable<ViewComponentSample.Models.TodoItem>
<h2> PVC Named Priority Component View</h2>
<h4>@ViewBag.PriorityMessage</h4>
<ul>
@foreach (var todo in Model)
{
<li>@todo.Name</li>
}
</ul>
更新Views/ToDo/Index.cshtml
:
@await Component.InvokeAsync("PriorityList", new { maxPriority = 4, isDone = true })
运行应用并验证 PVC 视图。
如果不呈现 PVC 视图,请验证是否调用优先级为 4 或更高的视图组件。
检查视图路径
将优先级参数更改为 3 或更低,从而不返回优先级视图。
暂时重命名为
Views/ToDo/Components/PriorityList/Default.cshtml
1Default.cshtml
.测试应用,你将收到以下错误:
An unhandled exception occurred while processing the request. InvalidOperationException: The view 'Components/PriorityList/Default' wasn't found. The following locations were searched: /Views/ToDo/Components/PriorityList/Default.cshtml /Views/Shared/Components/PriorityList/Default.cshtml EnsureSuccessful
将
Views/ToDo/Components/PriorityList/1Default.cshtml
复制到Views/Shared/Components/PriorityList/Default.cshtml
。将一些标记添加到共享 ToDo 视图组件视图,以指示视图来自“Shared”文件夹。
测试“共享”组件视图。
避免使用硬编码字符串
若要确保编译时的安全性,可以用类名替换硬编码的视图组件名称。 创建没有“ViewComponent”后缀的视图组件:
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ViewComponentSample.Models;
namespace ViewComponentSample.ViewComponents
{
public class PriorityList : ViewComponent
{
private readonly ToDoContext db;
public PriorityList(ToDoContext context)
{
db = context;
}
public async Task<IViewComponentResult> InvokeAsync(
int maxPriority, bool isDone)
{
var items = await GetItemsAsync(maxPriority, isDone);
return View(items);
}
private Task<List<TodoItem>> GetItemsAsync(int maxPriority, bool isDone)
{
return db.ToDo.Where(x => x.IsDone == isDone &&
x.Priority <= maxPriority).ToListAsync();
}
}
}
将 using
语句添加到 Razor 视图文件,并使用 nameof
运算符:
@using ViewComponentSample.Models
@using ViewComponentSample.ViewComponents
@model IEnumerable<TodoItem>
<h2>ToDo nameof</h2>
<!-- Markup removed for brevity. -->
<div>
@*
Note:
To use the below line, you need to #define no_suffix in ViewComponents/PriorityList.cs or it won't compile.
By doing so it will cause a problem to index as there will be multiple viewcomponents
with the same name after the compiler removes the suffix "ViewComponent"
*@
@*@await Component.InvokeAsync(nameof(PriorityList), new { maxPriority = 4, isDone = true })*@
</div>
可以使用采用 CLR 类型的 Component.InvokeAsync
方法的重载。 请记住,在本例中,使用 typeof
运算符:
@using ViewComponentSample.Models
@using ViewComponentSample.ViewComponents
@model IEnumerable<TodoItem>
<h2>ToDo typeof</h2>
<!-- Markup removed for brevity. -->
<div>
@await Component.InvokeAsync(typeof(PriorityListViewComponent), new { maxPriority = 4, isDone = true })
</div>
执行同步工作
如果不需要执行异步工作,框架将处理调用同步 Invoke
方法。 以下方法将创建同步 Invoke
视图组件:
public class PriorityList : ViewComponent
{
public IViewComponentResult Invoke(int maxPriority, bool isDone)
{
var items = new List<string> { $"maxPriority: {maxPriority}", $"isDone: {isDone}" };
return View(items);
}
}
视图组件 Razor 的文件列出了传递给 Invoke
方法的字符串 (Views/Home/Components/PriorityList/Default.cshtml
) :
@model List<string>
<h3>Priority Items</h3>
<ul>
@foreach (var item in Model)
{
<li>@item</li>
}
</ul>
例如,在文件中调用 Razor 视图组件 (, Views/Home/Index.cshtml
) 使用以下方法之一:
若要使用 IViewComponentHelper 方法,请调用 Component.InvokeAsync
:
@await Component.InvokeAsync(nameof(PriorityList), new { maxPriority = 4, isDone = true })
若要使用标记帮助程序,请使用 @addTagHelper
指令注册包含视图组件的程序集(视图组件位于名为 MyWebApp
的程序集中):
@addTagHelper *, MyWebApp
在 Razor 标记文件中使用视图组件标记帮助程序:
<vc:priority-list max-priority="999" is-done="false">
</vc:priority-list>
PriorityList.Invoke
的方法签名是同步的,但 Razor 在标记文件中使用 Component.InvokeAsync
找到并调用该方法。
所有视图组件参数都是必需的
视图组件中的每个参数都是必需的属性。 请参阅此 GitHub 问题。 如果省略任何参数:
InvokeAsync
方法签名不匹配,因此该方法将不会执行。- ViewComponent 不会呈现任何标记。
- 不会引发任何错误。