什么是TagHelper?这是ASP.NET Core 中新出现的一个名词,它的作用是使服务器端代码可以在Razor 文件中参与创建和呈现HTML 元素。(ASP.NET Core 系列目录)

一、概述

  上面的解释有点拗口?那么换一个名词,HtmlHelper大家都知道吧,在ASP.NET Core中,TagHelper类似HtmlHelper,但可以说是青出于蓝而胜于蓝。那么TagHelper的作用也就大概明白了吧。

首先通过一个例子看一下TagHelper是怎么使用的,看看它和HtmlHelper有什么区别。新建一个Book类:

    public class Book
{
[Display(Name = "编号")]
public string Code { get; set; }
[Display(Name = "名称")]
public string Name { get; set; }
}

新建对应的Controller和Action:

    public class BookController : Controller
{
// GET: /<controller>/
public IActionResult Index()
{
return View(new Book() { Code = "", Name = "ASP" });
}
}

最后就是View了:

@model Book
@{
Layout = null;
}
@Html.LabelFor(m => m.Name)
@Html.EditorFor(m => m.Name)
<br />
<label asp-for="Name"></label>
<input asp-for="Name" />

这里分别通过HtmlHelper和TagHelper两种方式实现了一个文本和输入框的显示。查看网页源代码,可以看到二者生成的HTML如下:

<label for="Name">Name</label>
<input class="text-box single-line" id="Name" name="Name" type="text" value="ASP" />
<br/>
<label for="Name">Name</label>
<input type="text" id="Name" name="Name" value="ASP" />

目前看起来二者差不多,从工作量上来看也是区别不大。现在功能实现了,需要做一些样式处理。简单举个例子,现在希望Book的编号(Code)对应的label的颜色设置为红色,定义了一个css如下:

<style type="text/css">
.codeColor {
color:red;
}
</style>

然后准备把这个样式应用到label上,这时如果是HtmlHelper就很有可能会被问:“class写在哪”,估计好多人都被问过。然后我们告诉他可以这样写:

@Html.LabelFor(m=>m.Name,new {@class="codeColor"}) 

前端工程师添加后达到了想要的效果,但同时表示记不住这个写法,下次可能还会问。

如果是TagHelper就方便了,告诉他可以像平时给Html的标签添加class一样操作即可,也就是:

<label asp-for="Name" class="codeColor"></label> 

前端工程师表示这种写法“真是太友好了”。同样对于Form及验证,比较一下两种写法的不同,HtmlHelper版:

@using (Html.BeginForm("Index", "Home", FormMethod.Post)){
@Html.LabelFor(m => m.Code)
@Html.EditorFor(m => m.Code) @Html.ValidationMessageFor(m => m.Code) <input type="submit" value="提交" />
}

TagHelper版:

<form asp-action="Index" asp-controller="Home" method="post">
<label asp-for="Code"></label>
<input asp-for="Code" />
<span asp-validation-for="Code"></span>
<input type="submit" value="提交" />
</form>

二、自定义TagHelper

现在有这样的需求,用于显示Book的编号的label不止要添加名为codeColor的css样式,还要给书的编号自动添加一个前缀,例如“BJ”。

对于这样的需求,希望可以通过一个简单的标记,然后由TagHelper自动实现。例如:

<label show-type="bookCode"></label>

自定义了一个属性“show-type”,用于标识这个label的显示类别,“1001”为假定的图书编号。通过这样的设置方式,将来如果需求有变化,需要对编号的显示做更多的修饰,只需修改对应的TagHelper即可,而页面部分不需要做任何调整。

系统提供了方便的自定义TagHelper的方式,就是继承系统提供的TagHelper类,并重写它的Process/ProcessAsync方法,例如下面的例子:

    public class LabelTagHelper : TagHelper
{
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
if (output.Attributes.TryGetAttribute("show-type", out TagHelperAttribute showTypeAttribute))
{
if (showTypeAttribute.Value.ToString().Equals("bookCode"))
{
output.Attributes.SetAttribute("class", "codeColor"); string content = output.Content.IsModified ? output.Content.GetContent() : (await output.GetChildContentAsync()).GetContent(); ;
output.Content.SetContent("BJ" + content);
}
}
}
}

首先判断label是否被设置了show-type="bookCode",然后获取当前label的Content内容,将其添加前缀“BJ”后作为此label的Content的新内容。注意一下Content的获取方式,如果它没有被修改,凭感觉直接通过output.Content.GetContent()获取到的内容是空的。

访问index页面,可以看到改标签已被处理,如下图:

备注:a.关于获取show-type的值,还可以有其他方式,下文会讲到。

b.从规范化命名的角度,建议将自定义的TagHelper其命名为XXXagHelper这样的格式。

三、TagHelper的注册

TagHelper自定义之后需要将其注册一下,否则它是不会生效的。打开_ViewImports.cshtml,默认为:

@using TagHelperDemo
@using TagHelperDemo.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

在最下面添加一条

@addTagHelper *, TagHelperDemo

最后添加的这一句话是什么意思呢?也就是将程序集TagHelperDemo(即第二个参数)中的所有TagHelper(第一个参数为“*”,即所有)全部启用。假如还定义了一个PasswordTagHelper,但只想只添加LabelTagHelper,可以这样写:

@addTagHelper TagHelperDemo.TagHelpers. LabelTagHelper, TagHelperDemo

如果想添加所有自定义的TagHelper,但要去除LabelTagHelper呢?

那么可以先添加所有,再去除这个LabelTagHelper。

@addTagHelper *, TagHelperDemo
@removeTagHelper TagHelperDemo.TagHelpers. LabelTagHelper, TagHelperDemo

四、TagHelper的作用范围

在项目中,可能不止使用label标签来显示Book的Code,还有可能会是p、span等类型的标签,现在的需求是,无论是上述哪一种标签,都要实现添加css和前缀的功能。

现在将index.cshtml中新增一个p标签如下:

<p show-type="bookCode"></p>

访问这个页面发现1002未被处理。这是因为我们定义的TagHelper名为LabelTagHelper,在默认的情况下只会处理label标签。当然也可以做特殊设置,例如下面代码的写法:

    [HtmlTargetElement("p")]
public class LabelTagHelper : TagHelper
{
//代码省略
}

通过“[HtmlTargetElement("p")]”指定本TagHelper只能被使用于p标签。再次访问此页面,发现p标签被处理了,而label未被处理。这说明这样的显式指定的优先级要高于默认的名称匹配。除了设置指定标签,还可以有一些其他的辅助设置:

[HtmlTargetElement("p", Attributes = "show-type", ParentTag = "div")]
public class LabelTagHelper : TagHelper

可以这样写,会匹配p标签,要求该标签拥有show-type属性,并且父标签为div。这几个条件是“and”的关系。如果还想匹配label标签,可以添加对label的设置,例如下面代码这样:

[HtmlTargetElement("p", Attributes = "show-type", ParentTag = "div")]
[HtmlTargetElement("label", Attributes = "show-type", ParentTag = "div")]
public class LabelTagHelper : TagHelper

这两个HtmlTargetElement的关系是“or”。通过这样的设置,可以极大的缩小目标标签的范围。

但是这样设置之后,这个TagHelper的名字再叫LabelTagHelper就不合适了,例如可以改为BookCodeTagHelper,最终代码如下:

[HtmlTargetElement("p", Attributes = "show-type", ParentTag = "div")]
[HtmlTargetElement("label", Attributes = "show-type", ParentTag = "div")]
public class BookCodeTagHelper : TagHelper
{
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
if (output.Attributes.TryGetAttribute("show-type", out TagHelperAttribute showTypeAttribute))
{
if (showTypeAttribute.Value.ToString().Equals("bookCode"))
{
output.Attributes.SetAttribute("class", "codeColor"); string content = output.Content.IsModified ? output.Content.GetContent() :
(await output.GetChildContentAsync()).GetContent(); ;
output.Content.SetContent("BJ" + content);
}
}
}
}

如果想使个别Html标签屏蔽TagHelper的作用,可以使用“!”。例如下面两个标签:

<!label show-type="bookCode"></label>

五、自定义标签

上一节最终形成了一个名为BookCodeTagHelper的TagHelper,我们知道LabelTagHelper是可以按名称默认匹配label标签的,那么是否可以自定义一个BookCode标签呢?在index.cshtml中添加这样的代码:

<BookCode></BookCode>

由于自定义bookcode标签的目的就是专门显示Book的Code,所以也不必添加show-type属性了。然后修改BookCodeTagHelper,修改后的代码如下:

public class BookCodeTagHelper : TagHelper
{
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
output.Attributes.SetAttribute("class", "codeColor"); string content = output.Content.IsModified ? output.Content.GetContent() :
(await output.GetChildContentAsync()).GetContent(); ;
output.Content.SetContent("BJ" + content);
}
}

去掉了两个HtmlTargetElement设置并取消了对show-type的判断,访问index页面查看新建的bookcode标签是否会被处理,结果是没有被处理。这是为什么呢?

这是由于TagHelper会将采用Pascal 大小写格式的类和属性名将转换为各自相应的短横线格式。即“BookCode”对应“book-code”,获取标签的属性值,同样遵循这样的规则。所以将标签改为如下写法即可:

<book-code></book-code>

再次运行测试,发现这个新标签被成功处理。查看网页源代码,被处理后的Html代码是这样的:

<book-code class="codeColor">TJ1003</book-code>

如果想将其改变为label,可以在BookCodeTagHelper中通过指定TagName实现:

public class BookCodeTagHelper : TagHelper
{
public Book Book { get; set; }
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
output.TagName = "label";
output.Attributes.SetAttribute("class", "codeColor"); string content = output.Content.IsModified ? output.Content.GetContent() :
(await output.GetChildContentAsync()).GetContent(); ;
output.Content.SetContent(Book.Prefix + content);
}
}

六、TagHelper与页面之间的数据传递

假如现在的新需求是图书编码的前缀不再固定为“BJ”了,需要在标签中定义,例如这样:

<book-code prefix="SH"></book-code>

需要获取prefix的值,在上面的例子中采用的是TryGetAttribute方法,其实还有简单的方式,修改BookCodeTagHelper,代码如下:

   public class BookCodeTagHelper : TagHelper
{
public string Prefix { get; set; }
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
output.Attributes.SetAttribute("class", "codeColor"); string content = output.Content.IsModified ? output.Content.GetContent() :
(await output.GetChildContentAsync()).GetContent(); ;
output.Content.SetContent(Prefix + content);
}
}

标签中的prefix的值会自动赋值给BookCodeTagHelper.Prefix,是不是更方便了。那么如果是Model中的值呢?假如Book类有一个属性“public string Prefix { get; set; } ”,这和传入一个字符串没什么区别,那么可以这样写:

<book-code prefix="@Model.Prefix"></book-code>

这种传值方式不止是支持字符串,将Model整体传入也是支持的,将标签修改如下:

<book-code book="@Model"></book-code>

修改BookCodeTagHelper代码:

    public class BookCodeTagHelper : TagHelper
{
public Book Book { get; set; }
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
output.Attributes.SetAttribute("class", "codeColor"); string content = output.Content.IsModified ? output.Content.GetContent() :
(await output.GetChildContentAsync()).GetContent(); ;
output.Content.SetContent(Book.Prefix + content);
}
}

七、取消标签输出

前面的几个例子都是对满足条件的标签的修改,TagHelper也可以取消对应标签的输出,例如存在这样一个标签:

<div simple-type="Simple1"></div>

如果不想让它出现在生成的Html中,可以这样处理:

[HtmlTargetElement("div",Attributes = "simple-type")]
public class Simple1TagHelpers : TagHelper
{
public string SimpleType { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
if (SimpleType.Equals("Simple1")) //可以是其他一些判断规则
{
output.SuppressOutput();
}
}
}

八、TagBuilder

在TagHelper中,可以用TagBuilder来辅助生成标签,例如存在如下两个div:

<div simple-type="Simple2"></div>
<div simple-type="Simple3"></div>

想在div中添加Html元素可以这样写:

[HtmlTargetElement("div",Attributes = "simple-type")]
public class Simple1TagHelpers : TagHelper
{
public string SimpleType { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
if (SimpleType.Equals("Simple2"))
{
output.Content.SetHtmlContent("<p>Simple2</p>");
}
else if (SimpleType.Equals("Simple3"))
{
var p = new TagBuilder("p");
p.InnerHtml.Append("Simple3");
output.Content.SetHtmlContent(p);
}
}
}

通过TagBuilder生成了一个新的p标签,并将它插入到div中。

ASP.NET Core 3.0 : 二十五. TagHelper的更多相关文章

  1. ASP.NET Core 3.0 : 二十八. 在Docker中的部署以及docker-compose的使用

    本文简要说一下ASP.NET Core 在Docker中部署以及docker-compose的使用  (ASP.NET Core 系列目录). 系统环境为CentOS 8 . 打个广告,求职中.. 一 ...

  2. ASP.NET Core 3.0 : 二十四. 配置的Options模式

    上一章讲到了配置的用法及内部处理机制,对于配置,ASP.NET Core还提供了一种Options模式.(ASP.NET Core 系列目录) 一.Options的使用 上一章有个配置的绑定的例子,可 ...

  3. 学习ASP.NET Core Razor 编程系列十五——文件上传功能(三)

    学习ASP.NET Core Razor 编程系列目录 学习ASP.NET Core Razor 编程系列一 学习ASP.NET Core Razor 编程系列二——添加一个实体 学习ASP.NET ...

  4. ASP.NET Core 2.2 : 二十六. 应用JWT进行用户认证

    本文将通过实际的例子来演示如何在ASP.NET Core中应用JWT进行用户认证以及Token的刷新方案(ASP.NET Core 系列目录) 一.什么是JWT? JWT(json web token ...

  5. ASP.NET Core 2.2 : 二十六. 应用JWT进行用户认证及Token的刷新

    来源:https://www.cnblogs.com/FlyLolo/p/ASPNETCore2_26.html 本文将通过实际的例子来演示如何在ASP.NET Core中应用JWT进行用户认证以及T ...

  6. ASP.NET Core 2.2 : 二十二. 多样性的配置方式

    大多数应用都离不开配置,本章将介绍ASP.NET Core中常见的几种配置方式及系统内部实现的机制. 说到配置,第一印象可能就是“.config”类型的xml文件或者“.ini”类型的ini文件,在A ...

  7. ASP.NET Core 2.2 : 二十. Action的多数据返回格式处理机制

    上一章讲了系统如何将客户端提交的请求数据格式化处理成我们想要的格式并绑定到对应的参数,本章讲一下它的“逆过程”,如何将请求结果按照客户端想要的格式返回去. 一.常见的返回类型 以系统模板默认生成的Ho ...

  8. ASP.NET Core 2.0: 二. 开发环境

    macOS:Install Visual Studio for Mac 系统要求: macOS 10.12 Sierra 及更高版本 其他要求: 可能会要求安装xcode或android相关环境, 详 ...

  9. 学习ASP.NET Core Razor 编程系列十九——分页

    学习ASP.NET Core Razor 编程系列目录 学习ASP.NET Core Razor 编程系列一 学习ASP.NET Core Razor 编程系列二——添加一个实体 学习ASP.NET ...

随机推荐

  1. vue 实现数据绑定原理

      案例: Vue 底层原理   // 目的: 使用原生js来实现Vue深入响应式   var box = document.querySelector('.box')   var button = ...

  2. Java网络编程 -- BIO 阻塞式网络编程

    阻塞IO的含义 阻塞(blocking)IO :阻塞是指结果返回之前,线程会被挂起,函数只有在得到结果之后(或超时)才会返回 非阻塞(non-blocking)IO :非阻塞和阻塞的概念相对应,指在不 ...

  3. 跟我学SpringCloud | 第十五篇:微服务利剑之APM平台(一)Skywalking

    目录 SpringCloud系列教程 | 第十五篇:微服务利剑之APM平台(一)Skywalking 1. Skywalking概述 2. Skywalking主要功能 3. Skywalking主要 ...

  4. Linux 用户和组 权限管理 常用命令与参数

    ========================================================================== 1.基本概念: 所有者 : 一般为文件的创建者,谁 ...

  5. 以太坊智能合约[ERC20]发币记录

    以太坊被称为区块链2.0,就是因为以太坊在应用层提供了虚拟机,使得开发者可以基于它自定义逻辑,通常被称为智能合约,合约中的公共接口可以作为区块链中的普通交易执行.本文就智能合约发代币流程作一完整介绍( ...

  6. 章节十六、5-TestNG高级功能--Part2

    一.测试用例的依赖关系--->(dependsOnMethods = {"依赖方法名"}) 1.在实现自动化的过程中,有些测试用例必须在其它测试用例执行之后才能运行,两者之间 ...

  7. JWT原理 使用(入门篇)

    1.JWT简介 JWT:Json Web Token,是基于Json的一个公开规范,这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息,他的两大使用场景是:认证和数据交换 使用起来就是,由 ...

  8. java实现截取PDF指定页并进行图片格式转换

    1.引入依赖 <dependency> <groupId>org.apache.pdfbox</groupId> <artifactId>pdfbox& ...

  9. 将excel中某列数据中,含有指定字符串的记录取出,并生成用这个字符串命名的txt文件

    Python 一大重要的功能,就是可处理大量数据,那分不开的即是使用Excel表格了,这里我做下学习之后的总结,望对我,及广大同仁们是一个帮助Python处理Excel数据需要用到2个库:xlwt 和 ...

  10. Python字典排序问题

    字典的问题 navagation: 1.问题来源 2.dict的学习 *3.numpy的应用 1.问题来源 在做cs231n,assigment1-kNN实现的时候,需要对一个列表中的元素进行计数,并 ...