什么是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. 数据算法 --hadoop/spark数据处理技巧 --(13.朴素贝叶斯 14.情感分析)

    十三.朴素贝叶斯 朴素贝叶斯是一个线性分类器.处理数值数据时,最好使用聚类技术(eg:K均值)和k-近邻方法,不过对于名字.符号.电子邮件和文本的分类,则最好使用概率方法,朴素贝叶斯就可以.在某些情况 ...

  2. EOS源码分析:transaction的一生

    最近在处理智能合约的事务上链问题,发现其中仍旧有知识盲点.原有的认识是一个事务请求会从客户端设备打包签名,然后通过RPC传到非出块节点,广播给超级节点,校验打包到可逆区块,共识确认最后变为不可逆区块. ...

  3. Python 字符串的所有方法详解

    name = "my name is {name} and my age is {age}" # 首字母大写 name.capitalize() # 统计某个字符的个数 name. ...

  4. Java进程故障排查

    故障分析 # 导致系统不可用情况(频率较大): 1)代码中某个位置读取数据量较大,导致系统内存耗尽,进而出现Full GC次数过多,系统缓慢: 2)代码中有比较消耗CPU的操作,导致CPU过高,系统运 ...

  5. poj3415_Common Substrings

    题意 给定两个字符串,求长度大于等于k的公共子串数. 分析 将两个字符串中间加个特殊字符拼接,跑后缀数组. 将题目转化为对每一个后缀求\(\sum_{j=1}^{i-1}lcp(i,j)\),且后缀\ ...

  6. 服务器小白的我,是如何将 node+mongodb 项目部署在服务器上并进行性能优化的

    前言 本文讲解的是:做为前端开发人员,对服务器的了解还是小白的我,是如何一步步将 node+mongodb 项目部署在阿里云 centos 7.3 的服务器上,并进行性能优化,达到页面 1 秒内看到 ...

  7. Java 并发:学习Thread 类

    Java 中 Thread类 的各种操作与线程的生命周期密不可分,了解线程的生命周期有助于对Thread类中的各方法的理解.一般来说,线程从最初的创建到最终的消亡,要经历创建.就绪.运行.阻塞 和 消 ...

  8. 自己实现vue瀑布流组件,含详细注释

    我知道vue有瀑布流插件vue-waterfall-easy,但是使用的时候与我的预期有部分别,所以就自己动手写了这个组件 人和动物的根本区别是是否会使用工具,我们不仅要会使用,还要会创造工具,别人提 ...

  9. 手把手教你用深度学习做物体检测(七):YOLOv3介绍

    YOLOv3 论文:< YOLOv3: An Incremental Improvement > 地址: https://arxiv.org/pdf/1804.02767.pdfyolov ...

  10. Mysql分区实战

    一,什么是数据库分区 前段时间写过一篇关于MySQL分表的的文章,下面来说一下什么是数据库分区,以mysql为例.mysql数据库中的数据是以文件的形势存在磁盘上的,默认放在/mysql/data下面 ...