视图组件

视图组件

作者: Rick Anderson
翻译: 娄宇(Lyrics)
校对: 高嵩

章节:

查看或下载示例代码

介绍视图组件

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

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

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

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

一个 视图组件 包含两个部分,类(通常派生自 ViewComponent )和它返回的结果(通常是一个视图)。类似控制器,视图组件可以是 POCO 类型,但是大部分开发者想要使用派生自 ViewComponent 的方法和属性。

创建视图组件

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

视图组件类

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

  • 派生自 ViewComponent
  • 使用 [ViewComponent] 特性装饰一个类,或者这个类的派生类。
  • 创建一个类,并以 ViewComponent 作为后缀。

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

一个视图组件类:

  • 完全支持构造函数依赖注入
  • 不参与控制器生命周期,意味着你不能在视图组件中使用过滤器

视图组件方法

视图组件在 InvokeAsync 方法中中定义逻辑,并返回 [IViewComponentResultIViewComponentResult。参数直接来自视图组件的调用,而不是来自模型绑定。视图组件从来不直接处理请求。通常视图组件初始化模型并通过调用 View方法传递它到视图。总之,视图组件方法有以下特点:

  • 定义一个 InvokeAsync 方法并返回 IViewComponentResult
  • 通常初始化模型并通过调用ViewComponent View方法传递它到视图
  • 参数来自调用方法,而不是 HTTP,没有模型绑定
  • 不可直接作为 HTTP 终结点,它们从你的代码中调用(通常在视图中)。视图组件从不处理请求
  • 重载的签名,而不是当前 HTTP 请求中的任何细节

视图搜索路径

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

  • Views/<controller_name>/Components/<view_component_name>/<view_name>
  • Views/Shared/Components/<view_component_name>/<view_name>

视图组件默认的视图名是 Default,意味着通常你的视图文件会命名为 Default.cshtml。当你创建视图组件结果或者调用 View 方法的时候,你可以指定不同的视图名。

我们建议你命名视图文件为 Default.cshtml 并且使用 Views/Shared/Components/<view_component_name>/<view_name> 路径。在这个例子中使用的 PriorityList 视图组件使用了 Views/Shared/Components/PriorityList/Default.cshtml 这个路径。

调用视图组件

要使用视图组件,从视图中调用 @Component.InvokeAsync("视图组件名", <匿名类型参数>)。参数将传递给 InvokeAsync 方法。在文章中开发的 PriorityList 视图组件被 Views/Todo/Index.cshtml 视图文件调用。在下面,使用了两个参数调用 InvokeAsync 方法:

复制代码

@await Component.InvokeAsync("PriorityList", new { maxPriority = 2, isDone = false })

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

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

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

复制代码

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

演练:创建一个简单的视图组件

下载,生成并测试启动代码。这是一个简单的项目,使用一个 Todo 控制器来显示 Todo 项列表。

添加一个视图组件类

创建一个 ViewComponents 文件夹并添加下面的 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 作为后缀结束,在运行时会从视图中使用 "PriorityList" 字符串来引用组件类。我会在后面详细解释。
  • [ViewComponent] 特性可以改变被用来引用视图组件的名字。比如,我们可以命名类为 XYZ,然后应用 ViewComponent 特性:

复制代码

[ViewComponent(Name = "PriorityList")]
public class XYZ : ViewComponent
  • 上面的 [ViewComponent] 特性告知视图组件选择器在寻找与组件相关的视图时使用名字 PriorityList,并且在从视图中引用组件类时使用 "PriorityList" 字符串。我会在后面详细解释。
  • 组件使用依赖注入使得数据上下文可用。
  • InvokeAsync 暴露一个可以在视图中调用的方法,并且它可以接受任意数量的参数。
  • InvokeAsync 方法返回没有完成并优先级小于等于 maxPriority 的 ToDo 项的集合。

创建视图组件 Razor 视图

  1. 创建 Views/Shared/Components 文件夹。这个文件夹 必须 命名为 Components。
  2. 创建 Views/Shared/Components/PriorityList 文件夹。这个文件夹必须和视图组件类名字匹配,或者是类名去掉后缀(如果我们遵循了约定并且使用 ViewComponent 作为类名后缀)。如果你使用 ViewComponent 特性,类名需要匹配特性中指定的名字。
  3. 创建一个 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 方法没有传递视图名(就像我们例子中),按照约定会使用 Default 作为视图名。在教程的后面部分,我会告诉你如何传递视图的名称。为特定控制器重写默认的样式,添加一个视图到特定控制器的视图文件夹(比如Views/Todo/Components/PriorityList/Default.cshtml)。

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

  1. 在 Views/Todo/index.cshtml 文件底部添加一个 div 包含调用 PriorityList 组件:

复制代码

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

标记 @Component.InvokeAsync 展示了调用视图组件的语法。第一个参数是我们想要调用的组件名。随后的参数传递给组件。InvokeAsync 可以接受任意数量的参数。

下面的图片显示了 Priority 项:

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

复制代码

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/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.cshtml 为 Temp.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.cshtml 到Views/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();
        }
    }
}

添加一个 using 语句到你的 Razor 视图文件并使用 nameof 操作符:

复制代码

@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>

附加的资源

返回目录

时间: 2024-08-07 08:36:25

视图组件的相关文章

7. Laravel5学习笔记:如何定义自己的视图组件

视图组件 视图组件就是在视图被渲染前,会调用的闭包或类方法.如果你想在每次渲染某些视图时绑定数据,视图组件可以把这样的程序逻辑组织在同一个地方. 对上面的话,理解如下: 这个php代码运行的时间是在渲染视图之前 使用这个组件应该用于每次渲染时,都要绑定数据的视图上.这样子就可以从控制器分离出数据绑定逻辑. 它很好的提现了 单一职责 ,对它的概念阐述 请看这里 使用 在laravel5的文档中已经说明了如何构建自己的视图组件.这里在重复一下. 先构建一个视图组件: <?php namespace

2.1安卓视图组件

1视图组件与容器组件 1)绝大部分安卓UI组件都存放与android.widget包及其子包,android.view及其子包 2)安卓所有 UI组件都继承自View类 3)View类有一个非常重要的子类:ViewGroup,但是ViewGroup经常作为其他组件的容器使用,安卓采用组合器模式设计View和ViewGroup VIewGroup可以当作View使用,但一般都是作为容器使用;但是ViewGroup是抽象类 实际都是用他的子类,如各种布局管理器 /** * <p> * A <

QT开发(八)——QT单元视图组件

QT开发(八)--QT单元视图组件 QT中有四种单元视图组件,分别为列表视图QListView.树形视图QTreeView.表格视图QTableView.列视图QColumnView. 一.QListView列表视图 1.QListView组件简介 QListView列表视图,继承自QAbstractItemView.QListView是基于模型的列表/图标视图,不显示表头和表框,为Qt的模型/视图结构提供了更灵活的方式. 2.QListView组件属性 QListView组件属性设置: A.n

Android系列 -- 2、视图组件View

1.视图组件View android 绝大部分的UI组件都放在android.widget包以及其子包.android.view包以及其子包中,android应用所有组件都继承了View类 View有一个非常重要的类:ViewGroup,但ViewGroup通常作为其他组件的容器使用

用 php 实现一个视图组件和模板引擎——基础

只要不是做后端接口开发和一些作为守护进程之类的服务器脚本,大多数时候都是在和浏览器打交道,因此合理组织并展现 html 标签是最为常见的工作.一般大家使用框架时,都会自带有一套视图组件和模板引擎. 我们不讨论这些组件和引擎的好坏.因为这些东西已经经过考验,可以在生产环境下使用.我们现在只是为了学习一些东西,这时候了解一些原理上的可能对以后的帮助更大,如果一些人真的很有时间,利用这些基础知识完全可以写一个自己的组件,即可当做练习,也可以拿去自用. 好了,说这么多,我还是希望很多人明白,视图和模板引

Box(视图组件)如何在多个页面不同视觉规范下的复用

本文来自 网易云社区 . 问题描述 Android App中的页面元素,都是由一个个Box(可以理解成一个个自定义View组件和Widget同级)组成,这些Box可以在不同的页面.不同的模块达到复用的效果.但是,现在遇到了一个对于开发复用棘手的问题, A页面的组件间距和B页面的组件间距可能不同. A页面的Box1与Box1间距,和Box1与Box2的间距不一样. Box和Box之间的分割线,有粗有细,有的有左边距. 等等还有许多需要动态调整的地方. 然后做这些Box组件,就是为了复用它们,但现在

ASP.NET Core Razor 视图组件

视图组件简介 在新的ASP.NET Core MVC中,视图组件类似于局部视图,但它们更强大.视图组件不使用模型绑定,仅依赖于您在调用时提供的数据. 视图组件特性: 呈现页面响应的某一部分而不是整个响应 包括在控制器和视图之间发现的关注分离和可测试性优势 可以具有参数和业务逻辑 通常在页面布局中调用 视图组件是在任何地方可重用的呈现逻辑,对于局部视图来说相对复杂,例如: 动态导航菜单 标签云(查询数据库) 登录面板 购物车 最近发表的文章 典型博客上的侧边栏内容 将在每个页面上呈现的登录面板,并

asp.net core 视图组件(转)

介绍视图组件 视图组件是 ASP.NET Core MVC 中的新特性,与局部视图相似,但是它们更加的强大.视图组件不使用模型绑定,只取决于调用它时所提供的数据.视图组件有以下特点: 渲染一个块,而不是整个响应 在控制器和视图之间同样包含了关注点分离和可测试性带来的好处 可以拥有参数和业务逻辑 通常从布局页调用 视图组件可以用在任何需要重复逻辑且对局部视图来说过于复杂的情况,比如: 动态导航菜单 标签云 (需要从数据库查询时) 登录面板 购物车 最近发表的文章 一个典型博客的侧边栏内容 会在所有

DjangoRestFramework的视图组件和路由组件

一视图组件 restframework可以轻松完成对某个数据的增删改查的目的,在继承viewsets.ModelViewset后,只需要配置数据管理对象和序列化器即可, class BookModelView(viewsets.ModelViewSet): queryset = models.Book.objects.all() serializer_class = BookSerializer 上面的方法是直接继承restframework提供的方法来实现对数据的增删改查的目的,根据前端提交请