ASP.NET Core Razor Pages 使用 视图(View) 组件

参考文章地址:为什么要在 ASP.NET Core 中使用视图组件 (telerik.com)

为什么使用视图组件而不是分部视图?最大的原因是,在 Razor 页面中插入分部视图时,与调用 View 关联的所有 ViewData 都会自动与分部视图关联。这意味着分部视图在一个 Razor 页面上的行为可能与在另一个页面上的行为大不相同。使用 View Components,您可以控制共享到 View Components 的内容。

视图组件是 ASP.NET Core 的新功能,旨在执行 ASP.NET 以前版本中的分部视图所做的所有事情,甚至更多。视图组件是完全独立的对象,可始终如一地从 Razor 视图呈现 html。它们是从派生自基类 ViewComponent 的 C# 类生成的,通常与 Razor 文件相关联以生成标记。

因此,View 组件可以很好地分离和封装渲染输出所需的服务器端逻辑。就像 ASP.NET MVC 控制器一样,视图组件很容易测试,因为它们可以编写为没有副作用,这意味着更少的错误。

如果您想继续学习,本文中使用的所有源代码都托管在 GitHub 上,地址为:

https://github.com/pkellner/progress-telerik-blog-viewcomponent

从头开始创建视图组件

在本文中,我将使用 Visual Studio 2017 创建 ASP.NET Core 2.1 网站。我可以使用命令行工具 (dotnet) 在 Windows 或 Mac 上轻松创建应用,但我选择使用 Visual Studio。

第一步是创建一个新的 ASP.NET Core 2.1 网站。

查看组件 1

对于 Web 应用程序的类型,我们将选择“Web 应用程序”,它将使用新的 ASP.NET Core Razor Pages 创建一个漂亮的简单网站。

查看组件 2

为我们搭建的目录结构包含一个名为“Pages”的目录,在其中,我们将创建一个新文件夹,我们将在其中创建一个新的视图组件(这确实是本文的重点)。

查看组件 3

在本文中,我们将开发一个非常有用的视图组件,可以在页面中反复使用该组件进行评级。由于视图组件通常同时具有 C# 文件和 Razor 视图页,因此让我们在组件目录中创建一个名为 RatingControl 的新目录,并在该目录中创建两个文件:RatingControlViewComponent.cs 和 Default.cshtml。

查看组件 4

有几种方法可以基于属性和命名约定创建视图控件。在我们的例子中,我将显式创建我的,它派生自类 ViewComponent,它有一个空构造函数和一个只有一个参数的 Invoke 方法。与从控制器调用并将模型参数作为输入的典型 Razor 页面不同,View 组件是通过调用类方法 Invoke 或 InvokeAsync 直接调用的,并带有值。您可以将这些值视为模型。

下面是简单的视图组件,该组件在调用时仅传递单个参数 (ratingControlType),然后将该参数作为模型传递给 default.cshtml Razor 视图。

using Microsoft.AspNetCore.Mvc;

namespace WebApp.Pages.Components.RatingControl
{
  public class RatingControlViewComponent : ViewComponent
  {
    public RatingControlViewComponent() { }
    public IViewComponentResult Invoke(string ratingControlType)
    {
      return View("Default", ratingControlType);
    }
  }
}
C#

Razor View 文件 Default.cshtml 只有两行。第一行表示传入的模型类型只是一个字符串,第二行表示该字符串包装在 h2 中。

@model string
<h2>@Model</h2>
标记

在 Razor 页面上使用 Outlook 上的视图组件和 InvokeAsync

至此,我们有了一个完整的视图组件。要让该视图组件为我们做任何事情,我们可以将其放在 Razor 页面上。我创建了一个名为 RatingDemoInvokeAsync 的新 Razor 视图页,此处显示在解决方案资源管理器中。

查看组件 5

文件本身使用 Component 方法 InvokeAsync,该方法引用 View Component 名称及其第一个参数,并且要将参数导入该方法,需要一个匿名类作为它的第二个参数。基本上,我们在这里只是使用 Razor 的功能。“@”符号将 Razor 视图页切换到 C#,然后接下来只是 C# 代码。下面是完整的 RatingDemoInvokeAsync.cshtml。

@page
@model WebApp.Pages.RatingDemoInvokeAsyncModel
@{
  vLayout = null;
}
<!DOCTYPE html>
<html>
<head>
  <meta name="viewport" content="width=device-width" />
  <title>starsdemo1</title>
</head>
<body>
  <h1>RatingDemoInvokeAsync</h1>
  @await Component.InvokeAsync("RatingControl", new {
    ratingControlType = "1to10"
  })
</body>
</html>
标记

运行此页面的输出仅显示标题为 RatingDemoInvokeAsync 的字符串“1to10”。这是因为我们的 View 组件所做的只是渲染传递到其中的字符串。

查看组件 6

在下一节中,我将介绍一种更简洁的方法来调用视图组件。也就是说,我们可以使用自己的自定义 HTML 元素(也称为标记帮助程序),而不是在 Razor 视图页中间使用 C#。

在带有标记帮助程序的 Razor 页面上使用视图组件

让我们首先看看使用标记帮助程序调用视图组件如何更改我们的 Razor 页面。在这里,我创建了一个新的 Razor 页面 RatingDemoTagHelper.cshtml。

@page
@model WebApp.Pages.RatingDemoTagHelperModel
@{
  Layout = null;
}
@addTagHelper *, WebApp
<!DOCTYPE html>
<html>
<head>
  <meta name="viewport" content="width=device-width" />
  <title>RatingDemoTagHelper</title>
</head>
<body>
  <h1>RatingDemoTagHelper</h1>
  <vc:rating-control rating-control-type="1to10">
  </vc:rating-control>
</body>
</html>
标记

我所做的只是添加了第 6 行,@addTagHelper它基本上说使用在我当前的 .NET 程序集中的任何位置定义的标记帮助程序。然后,我可以像在此 HTML 正文中一样引用评级控件,并使用 View 组件的标记帮助程序前缀 vc。这个名字只是我的视图组件名称,用烤肉串外壳拼写为评级控制。与其像上一节中的 invoke 方法那样使用丑陋的匿名类,不如将字符串传递给带有 HTML 属性的 View 组件,在 Kabob 的情况下也是如此,称为 rating-control-type。

这里的美妙之处在于,我们不必对视图组件进行任何修改即可使 Tag Helpers 工作。他们只是工作!运行页面当然会给我们提供与以前相同的输出,但我们已将 h1 内部 HTML 更改为 RatingDemoTagHelper。

查看组件 7


ASP.NET 核心基础知识:标记帮助程序

在我们的基础知识系列中了解有关 ASP.NET 核心标记助手的更多信息。


让我们的评级视图组件栩栩如生

到目前为止,我们还没有创建评级视图组件。我们只创建了一个组件来渲染传递到其中的内容。不是很有用。然而,我们已经创建了基础,所以现在我们可以构建一个真实世界的有用控件。与其从头开始,不如使用这个使用 jQuery 和 Bootstrap 的出色 MIT 许可评级控件。

https://github.com/antennaio/jquery-bar-rating

我不会让你对细节感到厌烦,但我基本上已经将这个项目带到了我的 ASP.NET 核心 Visual Studio 项目中,将一些 CSS 和 JavaScript 复制到 Web 项目 wwwroot 的基础中,如下所示。

查看组件 8

我创建了一个 Razor 视图页面,该页面只是从 jquery-bar-rating 存储库中的示例派生的静态 HTML。该文件是 Pages/RatingDemoRawHtml.cshtml,其中包含许多代码,如下所示:

查看组件 9

运行该页面会显示支持的几种评级类型:

查看组件 10

我们的目标是将 select 标签中的代码替换为 View 组件,该组件处理 HTML 选项的所有呈现,并处理必要的 JavaScript 和 jQuery。也就是说,完成后,我们将获得如下所示的 HTML(位于文件 pages/RatingDemoComplete.cshtml 中),而不是上面的原始 HTML 代码。

查看组件 11

请注意,这显示了三种不同的评级类型:“1to10”、“movie”和“pill”。我还包含了一个我们以前从未见过的 rating-control-id-value 属性。通常,当我们实现评级控件时,我们希望我们的控件能够在用户单击或更改评级(通常是 Ajax 调用)时回发回服务器端端点以更新数据。此值实质上允许您将唯一标识符传递给控件(通常作为 Razor 变量传入)。例如,如果您正在循环访问会议会话,并且想知道哪个评级控制与哪个会话相关联,则可以使用 rating-control-id-value 来传递会话 ID。

当我们运行此页面 (Pages/RatingDemoComplete.cshtml) 时,我们会看到三种评级类型(在我的示例中,我只为这三种类型编写代码,尽管 github 基础项目 jquery-bar-rating 支持六种类型):

查看组件 12

我们已经看到了呈现此内容的 Razor 页面,但这一切是从哪里来的?好吧,显而易见的答案是更新的视图组件。让我们看一下这段代码,并讨论它的作用。

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.Extensions.Configuration;
using System.Collections.Generic;
using System.Linq;
using WebApp.Models;

namespace WebApp.Pages.Components.RatingControl
{
  public class RingControlModel
  {
    public List<SelectListItem> SelectedListItems { get; set; }
    public string RatingControlType { get; set; }
    public string RatingControlValue { get; set; }
    public int RatingControlIdValue { get; internal set; }
  }

  public class RatingControlViewComponent : ViewComponent
  {
    private readonly RatingControlOptions _ratingControlOptions;

    public RatingControlViewComponent(IConfiguration config)
    {
      _ratingControlOptions = new RatingControlOptions
      {
        RatingControlType = config["RatingControlType"],
        RatingControlInitialValue1to10 = config["RatingControlInitialValue1to10"],
        RatingControlInitialValuePill = config["RatingControlInitialValuePill"],
        RatingControlInitialValueMovie = config["RatingControlInitialValueMovie"]
      };
    }

    public IViewComponentResult Invoke(string ratingControlType,int ratingControlIdValue)
    {
      var ratingControlValues = new List<string>();
      var ratingControlInitialValue = "";

      if (ratingControlType == "pill")
      {
        _ratingControlOptions.RatingControlValuesPill.ForEach(a => ratingControlValues.Add(a));
        ratingControlInitialValue = _ratingControlOptions.RatingControlInitialValuePill;
      }
      else if (ratingControlType == "1to10")
      {
        _ratingControlOptions.RatingControlValues1to10.ForEach(a => ratingControlValues.Add(a));
        ratingControlInitialValue = _ratingControlOptions.RatingControlInitialValue1to10;
      }
      else if (ratingControlType == "movie")
      {
        _ratingControlOptions.RatingControlValuesMovie.ForEach(a => ratingControlValues.Add(a));
        ratingControlInitialValue = _ratingControlOptions.RatingControlInitialValueMovie;
      }

      List<SelectListItem> ratings = ratingControlValues.Select(
        myValue => new SelectListItem
        {
          Value = myValue,
          Text = myValue,
          Selected = myValue.Equals(ratingControlInitialValue)
        }).ToList();

      RingControlModel ringControlModel = new RingControlModel
      {
        SelectedListItems = ratings,
        RatingControlType = ratingControlType,
        RatingControlValue = ratingControlInitialValue,
        RatingControlIdValue = ratingControlIdValue
      };

      return View(ringControlModel);
    }
  }
}
C#

首先请注意,我们已将程序配置信息注入到第 23 行的控件构造函数中。也就是说,我们从appsettings.json文件中提取设置数据。这样,我们就不必将代码显式添加到 invoke 方法来提取此数据。下面是我们的appsettings.json文件,它只是覆盖了默认控制类型以及所有三种评级类型的初始值。

{
  "RatingControlType": "1to10", // Default Type
  "RatingControlInitialValue1to10": "6",
  "RatingControlInitialValueMovie": "Good",
  "RatingControlInitialValuePill": "C",
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "AllowedHosts": "*"
}
JavaScript的

在我们的 Invoke 方法中,从第 34 行开始,我们只需根据配置数据为 select 标记创建选项。我不会在这里详细介绍,因为它对于理解 View 组件并不那么重要,但请随时自行查看 GitHub 中的代码(本文顶部提到的存储库包括所有工作代码)。

最后一部分是 Pages/Components/RatingControl/Default.cshtml,如下所示:

@model RingControlModel
@{
  var uniqueId = Guid.NewGuid().ToString("N");
}
<select id="@uniqueId"
        asp-for="@Model.RatingControlValue"
        asp-items="@Model.SelectedListItems"
        rating-control-id-value=@Model.RatingControlIdValue></select>
<script>
  $(document).ready(function () {
    $('#@uniqueId').barrating('show', {
      theme: 'bars-@Model.RatingControlType',
      onSelect: function(value) {
        alert('Selected rating: ' + value + ' id-value: ' + @Model.RatingControlIdValue);
      }
    });
  });
</script>
标记

此组件视图 Razor 页首先创建一个唯一 ID,以分配给评级控件。然后,它继续执行与基本 jquery-bar-rating 存储库相同的 jQuery,该存储库用于设置标准 html select 元素的样式和添加功能。您可以看到第 13 行的 jQuery 在 select 控件上执行方法“barrating()”。该方法调用样式并添加必要的事件处理,以便我们的组件视图既美观又正常工作。

我意识到我遗漏了很多关于jquery-bar-rating代码如何工作的细节。本文的真正内容是关于视图组件的工作原理以及如何将它们构建到代码中。这个条形码评级只是一个很好的工作示例,您可以自己学习。它展示了一个真实的示例,您可以根据该示例对代码进行建模,而不仅仅是一个简单的“hello world”示例,该示例通常很难扩展。

如果您有问题,请随时在下面发表评论或提出问题。我喜欢 ASP.NET Core 的新功能。View Components 和 Tag Helpers 只是使我们作为 Web 开发人员的生活变得更加轻松的两个新功能。

您可以在此处了解有关如何在 ASP.NET 项目中使用 Tag Helpers 的更多信息,如果您正在寻找一组 UI 组件来帮助您构建 ASP.NET Core 应用程序,请不要忘记查看 Telerik UI for ASP.NET Core,它支持各种 TagHelpers。您可以立即开始免费试用 30 天

posted @ 2024-05-06 11:31  LuoCore  阅读(211)  评论(0编辑  收藏  举报