ASP.NET Core 3.0 : 二十五. TagHelper
什么是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的更多相关文章
- ASP.NET Core 3.0 : 二十八. 在Docker中的部署以及docker-compose的使用
本文简要说一下ASP.NET Core 在Docker中部署以及docker-compose的使用 (ASP.NET Core 系列目录). 系统环境为CentOS 8 . 打个广告,求职中.. 一 ...
- ASP.NET Core 3.0 : 二十四. 配置的Options模式
上一章讲到了配置的用法及内部处理机制,对于配置,ASP.NET Core还提供了一种Options模式.(ASP.NET Core 系列目录) 一.Options的使用 上一章有个配置的绑定的例子,可 ...
- 学习ASP.NET Core Razor 编程系列十五——文件上传功能(三)
学习ASP.NET Core Razor 编程系列目录 学习ASP.NET Core Razor 编程系列一 学习ASP.NET Core Razor 编程系列二——添加一个实体 学习ASP.NET ...
- ASP.NET Core 2.2 : 二十六. 应用JWT进行用户认证
本文将通过实际的例子来演示如何在ASP.NET Core中应用JWT进行用户认证以及Token的刷新方案(ASP.NET Core 系列目录) 一.什么是JWT? JWT(json web token ...
- ASP.NET Core 2.2 : 二十六. 应用JWT进行用户认证及Token的刷新
来源:https://www.cnblogs.com/FlyLolo/p/ASPNETCore2_26.html 本文将通过实际的例子来演示如何在ASP.NET Core中应用JWT进行用户认证以及T ...
- ASP.NET Core 2.2 : 二十二. 多样性的配置方式
大多数应用都离不开配置,本章将介绍ASP.NET Core中常见的几种配置方式及系统内部实现的机制. 说到配置,第一印象可能就是“.config”类型的xml文件或者“.ini”类型的ini文件,在A ...
- ASP.NET Core 2.2 : 二十. Action的多数据返回格式处理机制
上一章讲了系统如何将客户端提交的请求数据格式化处理成我们想要的格式并绑定到对应的参数,本章讲一下它的“逆过程”,如何将请求结果按照客户端想要的格式返回去. 一.常见的返回类型 以系统模板默认生成的Ho ...
- ASP.NET Core 2.0: 二. 开发环境
macOS:Install Visual Studio for Mac 系统要求: macOS 10.12 Sierra 及更高版本 其他要求: 可能会要求安装xcode或android相关环境, 详 ...
- 学习ASP.NET Core Razor 编程系列十九——分页
学习ASP.NET Core Razor 编程系列目录 学习ASP.NET Core Razor 编程系列一 学习ASP.NET Core Razor 编程系列二——添加一个实体 学习ASP.NET ...
随机推荐
- linux下python中文乱码解决方案
1. 场景描述 linux服务器下安装了Anaconda3,执行Pyhton的K-means算法,结果出现如下图的中文字符乱码.上次已经解决了,忘记记录解决流程了,这次配置了一台新的服务器,又出现,默 ...
- MySQL数据库的安装和配置
MySQL数据库介绍 什么是数据库DB? DB的全称是database,即数据库的意思.数据库实际上就是一个文件集合,是一个存储数据的仓库,数据库是按照特定的格式把数据存储起来,用户可以对存储的数据进 ...
- Log2Net的部署和运维
前面几节中,笔者介绍了Log2Net组件的方方面面.但是,有人说,我用不起来,各种头疼的小问题.下面,我们就具体的看一看要怎么把这个组件应用到实际的项目中吧. 一.Log2Net组件的发布 前文中,我 ...
- C#开发BIMFACE系列6 服务端API之获取文件信息
在<C#开发BIMFACE系列4 服务端API之源上传文件>.<C#开发BIMFACE系列5 服务端API之文件直传>两篇文章中详细介绍了如何将本地文件上传到BIMFACE服务 ...
- NLP(八) 创建自然语言处理管道
一条管道可以被看作一个多阶段的数据流系统,其中一个组件的输出被视为另一个组件的输入 管道特点: 数据始终从一个组件流向另一个组件 组件是一个只考虑输入和输出数据的黑盒 NLP管道应有的功能: 采集输入 ...
- Scrum的三个仪式:Sprint规划会,Scrum每日站会,Sprint评审会
转自:http://blog.sina.com.cn/s/blog_6997f01501010m21.html Sprint Planning Meeting(Sprint规划会) 根据Product ...
- hdu 5977 Garden of Eden(点分治+状压)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5977 题解:这题一看就知道是状压dp然后看了一下很像是点分治(有点明显)然后就是简单的点分治+状压dp ...
- Codefroces 920F SUM and REPLACE(线段树)
SUM and REPLACE 题意:给你n个数,进行m次操作,分别是将区间[l,r]内的所有数替换成自己的因子数 和 对区间[l,r]进行求和. 题解:可以发现2的因子个数还是2,1的因子个数还是1 ...
- poj 2240 Arbitrage(Bellman_ford变形)
题目链接:http://poj.org/problem?id=2240 题目就是要通过还钱涨自己的本钱最后还能换回到自己原来的钱种. 就是判一下有没有负环那么就直接用bellman_ford来判断有没 ...
- android日志搜集原理及方案比较
说明: 本文只讨论Log日志,而不是应用的埋点日志. Android 日志架构 用一张图来了解Android Log的架构: 这里涉及到三个进程: APP进程: 调用Log的接口打日志,最终通过soc ...