ASP.NET Core 自定义Tag Helpers

在上一节中,我们介绍了ASP.NET Core内置的帮助标签,现在我们将学习自定义的帮助标签,使用C#代码转换成HTML代码,这一节我们通过大量的例子来了解自定义帮助标签

1 创建一个自定义帮助标签

我们在应用程序的根目录下创建一个CustomTagHelpers文件夹,接下来在文件夹内添加一个BackgroundColorTH.cs类,这个类的代码如下:

using Microsoft.AspNetCore.Razor.TagHelpers;namespace AspNetCore.CustomTagHelpers.CustomTagHelpers{    [HtmlTargetElement(Attributes = "background-color")]    public class BackgroundColorTH : TagHelper    {        public string BackgroundColor { get; set; }        public override void Process(TagHelperContext context, TagHelperOutput output)        {            output.Attributes.SetAttribute("class", $"btn btn-{BackgroundColor}");        }    }}

类上面的[HtmlTargetElement(Attributes = “background-color”)]特性告诉Tag Helpers可以在Html元素上使用backgroud-color特性
我们自定义的帮助标签继承自TagHelper基类,我们定义了一个BackgroundColor字符串类型的属性,dotnet 运行时将会把这个值从razor视图传输到这个属性

dotnet将检查自定义帮助标签类中的所有属性并且从视图将这些值转移到这些属性,如果他们名字和html元素属性名字匹配,dotnet将这些值转换成属性的类型,例如:当给HTML元素添加属性background-color=”danger”,BackgroundColor 属性值将会被自动赋值为danger属性有一个命名约定,即属性的名称应该是html样式,如背景颜色。我们不能让名称以 asp- 或 data- 开头

Tag Helper有一个Process函数,这个函数使用btn btn-{BackgroundColor}值给HTML元素添加CSS样式,并且html元素有一个backgroud-color特性

2 注册自定义帮助标签
在_ViewImports.cshtml文件中注册自定义帮助标签

@addTagHelper AspNetCore.CustomTagHelpers.CustomTagHelpers.*,AspNetCore.CustomTagHelpers

AspNetCore.CustomTagHelpers.CustomTagHelpers.* 通过使用通配符指定客户自定义帮助标签的命名空间,这意味着匹配AspNetCore.CustomTagHelpers.CustomTagHelpers命名空间下的所有文件,第二部分指定程序集的名称

3 使用自定义帮助标签
现在我们可以看到客户自定义的Tag Helper能工作,因此我们进入Home控制器的Create视图并且修改下面代码:

<button type="submit" background-color="danger">Add</button>

运行你的应用程序并且进入地址,你会发现红色的按钮在视图上,检查下面图片

图片

检查HTML源代码生成button为:

<a href="/Home/Index" class="btn btn-danger">取消</a>

我们能看到class=”btn btn-danger”样式被添加到button, btn-danger是bootstrap样式设置红色背景颜色,因此button为红色。这发生了什么?我们添加一个background-color=”danger”特性在button标签上,因此客户自定义标签类BackgroundColor属性自动接收特性的值。接下来处理函数添加CSS类btn btn-{property value} 到button ,按钮变成btn-danger

3.1 理解函数参数处理函数有2个参数类型:1 TagHelperContext
2 TagHelperOutputTagHelperContextProcess()函数的第一个参数是TagHelperContext对象类,该对象接收相关转换的元素的信息

TagHelperContext属性如下:

名称描述
AllAttributes它包含应用转换的元素的所有属性的只读字典
UniqueId它包含转换的元素的唯一标识符
Items它返回一个字典,用于在标签助手之间进行协作

TagHelperOutput 

该类对象包含要转换的HTML元素,转换过程通过配置它的对象来完成的

TagHelperOutput类属性

名称描述
TagName获取或设置输出元素的标签名称
Attributes返回一个字典包含所有输出元素的特性
Content设置输入元素的内容
PreElement输出元素以前插入元素
PostElement输出元素之后插入元素
PreContent在输出的已经存在内容之前插入内容
PostContent在输出的已存在的内容之后插入内容

TagHelperOutput 类方法

名称描述
SupressOuput()用于排除某个元素

4 管理Tag Helper的范围
我们可以管理标签助手的范围,即控制标签助手应用到哪个元素,这是使用 [HtmlTargetElement()]属性并且配合限制属性完成

名称描述
Attributes这个属性表明标签助手声明属性,可以以逗号分隔的方式提供多个属性,如果属性名称以”*”结尾,例如background-color-*,那么它将匹配background-color-*、background-color-white、background-color-black等
ParentTag此属性表明标签助手只能应用于给定父元素内
TagStructure该值由 TagStructure 枚举给出,该枚举定义了Unspecified、NormalOrSelfClosing和WithoutEndTag

客户自定义Tag Helper包含 [HtmlTargetElement()]特性,它告诉Tag Helper应用到任何HTML元素包含background-color特性

[HtmlTargetElement(Attributes = "background-color")]public class BackgroundColorTH: TagHelper{}

我们能添加更多的限制,下面代码添加一些限制-background-color帮助帮助标签只应用到button元素

[HtmlTargetElement("button", Attributes = "background-color")]public class BackgroundColorTH: TagHelper{}

现在我们在Tag Helper上添加一个条件,要求该button包含在form表单内部,这个完成通过添加ParentTag特性

[HtmlTargetElement("button", Attributes = "background-color", ParentTag = "form")]public class BackgroundColorTH: TagHelper{}

注意:你能应用多个[HtmlTargetElement()]属性到帮助标签,下面帮助标签同时应用到button和anchor标签有background-color特性

[HtmlTargetElement("button", Attributes = "background-color")][HtmlTargetElement("a", Attributes = "background-color")]public class BackgroundColorTH : TagHelper{}

我们也可以应用帮助标签到自定义的HTML元素并且将他们转换成HTML元素例如:我们添加自定义HTML元素在视图:

<aspbutton type="submit" background-color="danger" />

它不会呈现在浏览器,因为在HTML中没有aspbutton,然而使用自定义的帮助标签我们能转换这个自定义HTML元素到HTML button,让我们了解一下这个

在CustomTagHelpers文件内创建一个AspButtonTH.cs类并且添加下面代码

using Microsoft.AspNetCore.Razor.TagHelpers;
namespace AspNetCore.CustomTagHelpers.CustomTagHelpers{ [HtmlTargetElement("aspbutton")] public class AspButtonTH:TagHelper { public string Type { get; set; } = "Submit"; public string BackgroundColor { get; set; } = "primary"; public override void Process(TagHelperContext context, TagHelperOutput output) { output.TagName = "button"; output.TagMode = TagMode.StartTagAndEndTag; output.Attributes.SetAttribute("class", $"btn btn-{BackgroundColor}"); output.Attributes.SetAttribute("type", Type); output.Content.SetContent("Click to Add Record"); } }}

[HtmlTargetElement(“aspbutton”)]指定自定义标签应用aspbutton元素,我们添加2个属性到AspButtonTH.cs类,分别-Type和BackgroundColor,这些属性接收值从type和background-color 特性自定义的HTML元素

我们看到内部处理方法- 

1 TagName属性指定aspbutton是一个html的button元素,TagMode属性指定元素属性写入开始和结束标签 例如:<button></button>

2 我们使用SetAttribute()方法设置元素的属性以及其值

3 最后使用SetContent()方法,我们给button添加”Click to Add Record”的内容在Views/Home/Create.cshtml中使用下面元素替换button

<aspbutton type="submit" background-color="danger" />

运行应用程序并且访问https://localhost:7270/Home/Create ,你将看到HTML元素呈现一个button,如下图所示:

图片

检查生成的HTML代码

<button class="btn btn-danger" type="submit">Click to Add Record</button>

5 使用帮助标签附件元素TagHelperOutput类的PreElement和PostElement属性在输出元素的前或者后添加元素,为帮助理解他们,在CustomTagHelpers文件夹下并且命名为PrePostElementTH.cs,添加下面代码:

using Microsoft.AspNetCore.Mvc.Rendering;using Microsoft.AspNetCore.Razor.TagHelpers;
namespace AspNetCore.CustomTagHelpers.CustomTagHelpers{ [HtmlTargetElement("div", Attributes = "pre-post")] public class PrePostElementTH : TagHelper { public bool AddHeader { get; set; } = true; public bool AddFooter { get; set; } = true; public string PrePost { get; set; } public override void Process(TagHelperContext context, TagHelperOutput output) { output.Attributes.SetAttribute("class", "m-1 p-1");
TagBuilder title = new TagBuilder("h1"); title.InnerHtml.Append(PrePost); TagBuilder container = new TagBuilder("div"); container.Attributes["class"] = "bg-info m-1 p-1"; container.InnerHtml.AppendHtml(title);
if (AddHeader) output.PreElement.SetHtmlContent(container); if (AddFooter) output.PostElement.SetHtmlContent(container); } }}

在div元素定义pre-post特性,帮助标签使用PreElement 和PostElement 属性在输出元素的头或者尾添加元素我们使用Microsoft.AspNetCore.Mvc.Rendering命名空间下的TagBuilder类创建HTML元素,使用TagBuilder类创建元素:h1包含在div元素内部
div包含h1元素,我们给div元素提供了Bootstrap类样式我们有个可选的bool类型add-header和add-footer指定是否排除header和footer,默认是包含header和footer
最后编辑_Layout.cshtml视图,将pre-post指令添加到div内部,如下代码所示:

    <div pre-post="Tag Helpers">        <main role="main" class="pb-3">            @RenderBody()        </main>    </div>

运行应用程序,你将会看到如下页面

图片

如下为生成的HTML代码

<div class="bg-info m-1 p-1"><h1>Tag Helpers</h1></div>
<div class="bg-info m-1 p-1"><h1>Tag Helpers</h1></div>

6 使用Tag Helper添加内容我们使用PreContext和PostContext在输出元素中插入内容PreContent :  在已经存在的内容之前插入新内容
PostContent : 在已经存在的内容之后插入新内容在CustomTagHelpers文件夹下新建PrePostContentTH.cs 文件,代码如下:

using Microsoft.AspNetCore.Razor.TagHelpers;namespace AspNetCore.CustomTagHelpers.CustomTagHelpers{    [HtmlTargetElement("td", Attributes = "underline")]    public class PrePostContentTH : TagHelper    {        public override void Process(TagHelperContext context, TagHelperOutput output)        {            output.PreContent.SetHtmlContent("<u>");            output.PostContent.SetHtmlContent("</u>");        }    }}

这个帮助标签在td元素添加下划线,并且插入一个<u>html元素,在table单元格中测试该特性

运行应用程序你将会看到名称列下面有个下划线

图片

检查生成的HTML

图片

6.1 TagHelperContext.Items 属性在两个Tag Helpers之间工作

TagHelperContext.Items 属性使用在两个tag helpers之间工作,为了演示创建一个新文件叫CoordinateTagHelpers.cs文件,在CustomTagHelpers文件夹下并且添加下面代码:

using Microsoft.AspNetCore.Razor.TagHelpers;namespace AspNetCore.CustomTagHelpers.CustomTagHelpers{    [HtmlTargetElement("div", Attributes = "theme")]    public class ParentThemeTH : TagHelper    {        public string Theme { get; set; }        public override void Process(TagHelperContext context, TagHelperOutput output)        {            context.Items["theme"] = Theme;        }    }    [HtmlTargetElement("button", ParentTag = "div")]    [HtmlTargetElement("a", ParentTag = "div")]    public class ChildThemeTH : TagHelper    {        public override void Process(TagHelperContext context, TagHelperOutput output)        {            if (context.Items.ContainsKey("theme"))                output.Attributes.SetAttribute("class", $"btn btn-{context.Items["theme"]}");        }    }}

1 ParentThemeTH – 给div元素应用一个theme的值,它添加theme特性值到Items字典,为了能够使div元素内部的元素是可用的

2 ChildThemeTH – 操作div内部的button和anchor元素,这个帮助标签获取父元素的值从div中,并将相同的样式设置给子元素
现在测试这个功能- 在Create视图中替换button使用下面代码

 <div class="col-sm-11 offset-sm-1" theme="secondary">        <button type="submit" class="btn btn-primary">新增</button>        <a class="btn btn-default" href="/Home/Index">取消</a>    </div>

现在我们看到button和anchor标签使用secondary bootstrap样式

图片

7 获取视图上下文数据在Tag Helpers

在Tag Helpers内部我们能够获取到详细的视图数据, 视图上下文数据routing,ViewData,ViewBag,TempData,ModelState,当前Http 请求等

为了获取视图上下文数据添加一个ViewContextData的属性,并且在该属性上添加两个attribute:

[ViewContext][HtmlAttributeNotBound]public ViewContext ViewContextData { get; set; }

ViewContext特性表明当一个新的TagHelper类的实例被创建,这个属性的值将被赋值给ViewContext对象,
HtmlAttributeNotBound特性表示如果有view-context特性在html元素上,将不会给这个属性赋值让我们创建一个帮助标签获取ViewContext数据,在CustomTagHelpers文件夹下添加一个FormTH.cs ,添加下面代码

using Microsoft.AspNetCore.Mvc.Rendering;using Microsoft.AspNetCore.Mvc.Routing;using Microsoft.AspNetCore.Mvc.ViewFeatures;using Microsoft.AspNetCore.Mvc;using Microsoft.AspNetCore.Razor.TagHelpers;
namespace AspNetCore.CustomTagHelpers.CustomTagHelpers{ [HtmlTargetElement("form")] public class FormTH: TagHelper { private IUrlHelperFactory urlHelperFactory; public FormTH(IUrlHelperFactory factory) { urlHelperFactory = factory; }
[ViewContext] [HtmlAttributeNotBound] public ViewContext ViewContextData { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output) { IUrlHelper urlHelper = urlHelperFactory.GetUrlHelper(ViewContextData); output.Attributes.SetAttribute("action", urlHelper.Action(ViewContextData.RouteData.Values["action"].ToString(), ViewContextData.RouteData.Values["controller"].ToString())); } }}

这个帮助标签适用于所有的表单元素,主要工作是-根据路由数据创建表单action方法的URLProcess方法内部获取IUrlHelper对象使用urlHelperFactory的GetUrlHelper()方法,IUrlHelper类使用构建基于路由的URL,代码如下:

urlHelper.Action(ViewContextData.RouteData.Values["action"].ToString(), ViewContextData.RouteData.Values["controller"].ToString())

现在进入Create视图并且修改表单tag包含method属性如下:

<form method="post">
</form>

运行应用程序,进入 https://localhost:7270/Home/Create检查生成的HTML表单,如下:

图片

8 阻止元素输出

TagHelperOutput类的SupressOutput()方法阻止视图中的元素输出,让我们创建一个新的自定义帮助标签,这个帮助标签会根据请求的action方法来显示div进入_Layout.cshtml文件并且在@RenderBody()前包含一个新的div

 <div action-name="Index" class="bg-danger">        <h2>Hello</h2>   </div>

在CustomTagHelpers文件夹中创建SuppressOutputTH.cs类,包含下面代码:

using Microsoft.AspNetCore.Mvc.Rendering;using Microsoft.AspNetCore.Mvc.ViewFeatures;using Microsoft.AspNetCore.Razor.TagHelpers;
namespace AspNetCore.CustomTagHelpers.CustomTagHelpers{ [HtmlTargetElement(Attributes = "action-name")] public class SuppressOutputTH:TagHelper { public string ActionName { get; set; }
[ViewContext] [HtmlAttributeNotBound] public ViewContext ViewContext { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output) { if (!ViewContext.RouteData.Values["action"].ToString().Equals(ActionName)) output.SuppressOutput(); } }}

这个帮助标签使用ViewContext从路由数据中获取action值,使用这个值和div元素中提供的action的名字作比较,如果不匹配则调用SuppressOutput()方法

现在运行应用程序,你将会看到新的div显示在Index视图并且没有在Create视图中显示:

图片
图片

总结

在这节我们覆盖了所有的关于客户自定义标签,这是ASP.NET Core在服务器端实现客户自定义逻辑一种比较好的方式源代码地址https://github.com/bingbing-gui/Asp.Net-Core-Skill/tree/master/Fundamentals/AspNetCore.TagHelpers/AspNetCore.CustomTagHelpers

参考文献

[1]https://www.yogihosting.com/aspnet-core-custom-tag-helpers/

声明:文中观点不代表本站立场。本文传送门:https://eyangzhen.com/309426.html

联系我们
联系我们
分享本页
返回顶部