ASP.NET Core 中文文档 第四章 MVC(3.9)视图组件

章节:

介绍视图组件

视图组件是 ASP.NET Core MVC 中的新特性,与局部视图相似,但是它们更加的强大。视图组件不使用模型绑定,只取决于调用它时所提供的数据。视图组件有以下特点:

  • 渲染一个块,而不是整个响应
  • 在控制器和视图之间同样包含了关注点分离和可测试性带来的好处
  • 可以拥有参数和业务逻辑
  • 通常从布局页调用

视图组件可以用在任何需要重复逻辑且对局部视图来说过于复杂的情况,比如:

  • 动态导航菜单
  • 标签云 (需要从数据库查询时)
  • 登录面板
  • 购物车
  • 最近发表的文章
  • 一个典型博客的侧边栏内容
  • 会在所有页面渲染的登录面板,根据用户登录状态显示登录或者注销
ViewComponentViewComponent
创建视图组件

这个章节包含创建视图组件的高级需求。在稍后的文章中,我们将详细地检查每一个步骤,并创建一个视图组件。

视图组件类

一个视图组件类可以由以下任何一个方式创建:

ViewComponent[ViewComponent]

如同控制器一样,视图组件必须是公开的,非嵌套的,以及非抽象的类。视图组件名称是类名并去掉“ViewComponent”后缀。也可以通过 ViewComponentAttribute.Name 属性进行明确的指定。

一个视图组件类:

视图组件方法

InvokeAsync
InvokeAsyncIViewComponentResult

视图搜索路径

运行时对视图的搜索路径如下:

  • Views/<controller_name>/Components/<view_component_name>/<view_name>
  • Views/Shared/Components/<view_component_name>/<view_name>
View
PriorityList
调用视图组件
@Component.InvokeAsync("视图组件名", <匿名类型参数>)InvokeAsyncPriorityListInvokeAsync
@await Component.InvokeAsync("PriorityList", new { maxPriority = 2, isDone = false })

从控制器直接调用视图组件

视图组件通常从视图中调用,但是你也可以从控制器方法中直接调用。当视图组件没有像控制器一样定义终结点时,你可以简单实现一个控制器的 Action ,并使用一个 ViewComponentResult作为返回内容。

在这例子中,视图组件通过控制器直接调用:

 public IActionResult IndexVC()
{
return ViewComponent("PriorityList", new { maxPriority = 3, isDone = false });
}
演练:创建一个简单的视图组件
Todo

添加一个视图组件类

PriorityListViewComponent
using Microsoft.AspNet.Mvc;
using Microsoft.Data.Entity;
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]XYZViewComponent
[ViewComponent(Name = "PriorityList")]
public class XYZ : ViewComponent
[ViewComponent]PriorityListInvokeAsyncInvokeAsyncmaxPriorityToDo

创建视图组件 Razor 视图

ViewComponent
@model IEnumerable<ViewComponentSample.Models.TodoItem>
<h3>Priority Items</h3>
<ul>
@foreach (var todo in Model)
{
<li>@todo.Name</li>
}
</ul>
TodoItemInvokeAsync

如果视图组件是特定控制器的,你可以添加到特定控制器文件夹(Views/Todo/Components/PriorityList/Default.cshtml)。

div
  }
</table>
<div >
@await Component.InvokeAsync("PriorityList", new { maxPriority = 2, isDone = false })
</div>
@Component.InvokeAsyncInvokeAsync

下面的图片显示了 Priority 项:

你也可以从控制器直接调用视图组件:

public IActionResult IndexVC()
{
return ViewComponent("PriorityList", new { maxPriority = 3, isDone = false });
}

指定视图名

InvokeAsyncPriorityListViewComponentInvokeAsync
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/TodoList/Index.cshtml

</table>
<div>
@await Component.InvokeAsync("PriorityList", new { maxPriority = 4, isDone = true })
</div>

运行应用程序并验证 PVC 视图。

如果 PVC 视图没有渲染,请验证你是否使用 4 或者更高 priority 参数来调用视图组件。

检查视图路径

  1. 改变 priority 参数到 3 或者更低,使得不返回优先级视图。

  2. 暂时重命名 Views/Todo/Components/PriorityList/Default.cshtmlTemp.cshtml

  3. 测试应用程序,你将得到以下错误:

    An unhandled exception occurred while processing the request.

    InvalidOperationException: The view 'Components/PriorityList/Default'

    was not found. The following locations were searched:

    /Views/ToDo/Components/PriorityList/Default.cshtml

    /Views/Shared/Components/PriorityList/Default.cshtml.

    Microsoft.AspNetCore.Mvc.ViewEngines.ViewEngineResult.EnsureSuccessful()

  4. 复制 Views/Shared/Components/PriorityList/Default.cshtmlViews/Todo/Components/PriorityList/Default.cshtml

  5. 添加一些标记到 Todo 视图组件视图来表明视图是来自 Todo 文件夹。

  6. 测试 非共享 组件视图。

避免魔法字符串

如果你想编译时安全你可以用类名替换硬编码视图组件名。创建视图组件不以 "ViewComponent" 作为后缀:

using Microsoft.AspNet.Mvc;
using Microsoft.Data.Entity;
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();
}
}
}
usingnameof
@using ViewComponentSample.Models
@using ViewComponentSample.ViewComponents
@model IEnumerable<TodoItem> <h2>ToDo nameof</h2>
<!-- Markup removed for brevity. -->
}
</table> <div> @await Component.InvokeAsync(nameof(PriorityList), new { maxPriority = 4, isDone = true })
</div>
附加的资源