众所周知,在asp.net core中编写Razor视图的时候,用了一种新的写法--TagHelper

那这个TagHelper是怎么回事呢?

首先来看看TagHelper的项目位置,它是位于Microsoft.AspNetCore.Mvc.TagHelpers。

如果看到project.json,可以发现,它还依赖一个比较重要的东西Microsoft.AspNetCore.Mvc.Razor

为什么这么说呢,其实很简单,看了里面诸多TagHelper,就会发现,里面都是继承了

Microsoft.AspNetCore.Razor.TagHelpers下面的TagHelper这个抽象类。

下面就以我们天天用到的表单--FormTagHelper为例来说一下,他是怎么实现的。

首先要看看TagHelper这个抽象类:

public abstract class TagHelper : ITagHelper

{

protected TagHelper();

public virtual int Order { get; }

public virtual void Init(TagHelperContext context);

public virtual void Process(TagHelperContext context, TagHelperOutput output);

public virtual Task ProcessAsync(TagHelperContext context, TagHelperOutput output);

}

面包含两比较重要的方法:Process和ProcessAsync

其实看方法名就应该知道一个是同步的方法一个是异步的方法

因为这个是输出html的方法,你说,这能不重要吗?下面来看看FormTagHelper的具体实现吧!

[HtmlTargetElement("form", Attributes = ActionAttributeName)]

先来看看HtmlTargetElement这个Attribute是用来干嘛的

简单来说,它指定了我们html标签(<form></form>)以及一些相关的元素。

可以看到,诸多Attributes = XXXAttributeName,其中的XXXAttributeName是在类里面定义的变量。

private const string ActionAttributeName = "asp-action";

private const string AntiforgeryAttributeName = "asp-antiforgery";

private const string AreaAttributeName = "asp-area";

private const string ControllerAttributeName = "asp-controller";

private const string RouteAttributeName = "asp-route";

private const string RouteValuesDictionaryName = "asp-all-route-data";

private const string RouteValuesPrefix = "asp-route-";

private const string HtmlActionAttributeName = "action";

再来看看下面的图,相对比一看,是不是就很清晰了呢?

我们可以看到下面的好几个属性,如Controller,它的上面是有 HtmlAttributeName来标注的

而且这个指向的名字还是ControllerAttributeName(也就是asp-controller)。这个就是用来接收asp-controller的值。

[HtmlAttributeName(ControllerAttributeName)]public string Controller { get; set; }

相对来说,这样做只是起了个别名。

[HtmlTargetElement("form", Attributes = ActionAttributeName)]

[HtmlTargetElement("form", Attributes = AntiforgeryAttributeName)]

[HtmlTargetElement("form", Attributes = AreaAttributeName)]

[HtmlTargetElement("form", Attributes = ControllerAttributeName)]

[HtmlTargetElement("form", Attributes = RouteAttributeName)]

[HtmlTargetElement("form", Attributes = RouteValuesDictionaryName)]

[HtmlTargetElement("form", Attributes = RouteValuesPrefix + "*")]

public class FormTagHelper : TagHelper

当然,我们也是可以不指定别名的,也可以不用在HtmlTargetElement指明Attributes

好比如下的代码,就可以直接用Controller

 [HtmlTargetElement("form")]  public class FormTagHelper : TagHelper  {       public string Controller { get; set; }  }

还有一个RouteValues的属性,它是一个键值对,用来存放参数的,具体可以怎么用呢?

总的来说有两种用法。可以看到它指向asp-all-route-data和asp-route-

1 [HtmlAttributeName(RouteValuesDictionaryName, DictionaryAttributePrefix = RouteValuesPrefix)]

用法如下:一种是用asp-all-route-data来接收一个IDictionary类型的变量,一种是通过asp-route-*的方式来接收参数*的值。

这两种写法是等价的。

下面就是FormTagHelper的构造函数和一个Generator属性

 public FormTagHelper(IHtmlGenerator generator) {   Generator = generator;  }   protected IHtmlGenerator Generator { get; }

由于在Core中,依赖注入随处可见,看到这个写法马上就是想到了这个

果不其然,发现其对应了一个实现类:DefaultHtmlGenerator。

public class DefaultHtmlGenerator : IHtmlGenerator

{

public DefaultHtmlGenerator(IAntiforgery antiforgery, IOptions<MvcViewOptions> optionsAccessor, IModelMetadataProvider metadataProvider, IUrlHelperFactory urlHelperFactory, HtmlEncoder htmlEncoder, ClientValidatorCache clientValidatorCache);

public virtual TagBuilder GenerateActionLink(ViewContext viewContext, string linkText, string actionName, string controllerName, string protocol, string hostname, string fragment, object routeValues, object htmlAttributes);

public virtual IHtmlContent GenerateAntiforgery(ViewContext viewContext);

public virtual TagBuilder GenerateForm(ViewContext viewContext, string actionName, string controllerName, object routeValues, string method, object htmlAttributes);

public virtual TagBuilder GenerateLabel(ViewContext viewContext, ModelExplorer modelExplorer, string expression, string labelText, object htmlAttributes);

public virtual TagBuilder GenerateTextArea(ViewContext viewContext, ModelExplorer modelExplorer, string expression, int rows, int columns, object htmlAttributes);

public virtual TagBuilder GenerateTextBox(ViewContext viewContext, ModelExplorer modelExplorer, string expression, object value, string format, object htmlAttributes);

protected virtual TagBuilder GenerateInput(ViewContext viewContext, InputType inputType, ModelExplorer modelExplorer, string expression, object value, bool useViewData, bool isChecked, bool setId, bool isExplicitValue, string format, IDictionary<string, object> htmlAttributes);

protected virtual TagBuilder GenerateLink(string linkText, string url, object htmlAttributes);

     ....省略部分

}

这个类里面,我们看到了熟悉的TagBuilder,就算不去看它里面的实现都能知道它是用来干嘛的

它就是用来创建我们的Html标签,相信用过MVC的,多多少少都扩展过HtmlHelper,这是类似的。

最后,也是最最重要的重写的Process方法。

可以看到开始就判断了表单<form>中是否包含了action这个属性output.Attributes.ContainsName(HtmlActionAttributeName)

如果包含,就是正常的html标签。换句话说,正常的html写法和我们的TagHelper方法会有冲突,只能用其中一种。

当我们这样写的时候,编译能通过。

但是,运行的时候就会出错。

再下面的处理就是用了TagBuilder去处理了。

收集路由的数据放到一个字典中->区域是否存在->用Generator去创建form表单,返回TagBuilder对象->TagHelperOutput对象把tagbuilder的innerhtml等信息输出。

如下面的写法:

1 <form method="post" asp-action="Get" asp-controller="Product" asp-antiforgery="false" asp-route-id="2">2   <button type="submit">submit</button>3 </form>

生成对应的html如下:

1 <form method="post" action="/Product/Get/2">2   <button type="submit">submit</button>3 </form>

到这里,FormTagHelper的讲解就算是OK,至于其他的,原理都是差不多,就不再累赘了。

来看看,到底有多少种TagHelper(还没有部分没有列出来),以及它们包含的属性。

下面是我们自己写一个TagHelper——CatcherATagHelper,这个TagHelper是干什么的呢?它只是一个精简版的A标签。

using Microsoft.AspNetCore.Mvc;

using Microsoft.AspNetCore.Mvc.Rendering;

using Microsoft.AspNetCore.Mvc.Routing;

using Microsoft.AspNetCore.Mvc.TagHelpers;

using Microsoft.AspNetCore.Mvc.ViewFeatures;

using Microsoft.AspNetCore.Razor.TagHelpers;

namespace Catcher.EasyDemo.Controllers.TagHelpers

{

[HtmlTargetElement("catcher-a")]

public class CatcherATagHelper:TagHelper

{

public CatcherATagHelper(IHtmlGenerator generator, IUrlHelperFactory urlHelperFactory)

{

this.Generator = generator;

UrlHelperFactory = urlHelperFactory;

}

[HtmlAttributeNotBound]

public IUrlHelperFactory UrlHelperFactory { get; }

protected IHtmlGenerator Generator { get; }

public override int Order

{

get

{

return -1000;

}

}

public string Action { get; set; }

public string Controller { get; set; }

public string LinkText { get; set; }

[ViewContext]

[HtmlAttributeNotBound]

public ViewContext ViewContext { get; set; }

public override void Process(TagHelperContext context, TagHelperOutput output)

{

//method 1

if (Action != null || Controller != null)

{

output.Attributes.Clear();

var urlHelper = UrlHelperFactory.GetUrlHelper(ViewContext);

output.TagName = "a";

output.Attributes.SetAttribute("href", urlHelper.Action(Action, Controller));

//whether the inner html is null

if (output.Content.IsEmptyOrWhiteSpace)

{

output.PreContent.SetContent(LinkText);

}

}

//method 2

//TagBuilder tagBuilder;

//if (Action != null || Controller != null)

//{

//    tagBuilder = Generator.GenerateActionLink(

//            ViewContext,

//            linkText: string.Empty,

//            actionName: Action,

//            controllerName: Controller,

//            protocol: string.Empty,

//            hostname: string.Empty,

//            fragment: string.Empty,

//            routeValues: null,

//            htmlAttributes: null);

//    output.TagName = "a";

//    //whether the inner html is null

//    if (output.Content.IsEmptyOrWhiteSpace)

//    {

//        output.PreContent.SetContent(LinkText);

//    }

//    output.MergeAttributes(tagBuilder);

//}

}

}

}

这里提供了两种写法供大家参考

一种是借助IUrlHelperFactory去生成链接

一种是借助IHtmlGenerator去生成链接

写好了之后要怎么用呢?

不知道大家有没有留意_ViewImports.cshtml这个文件

1 @using Catcher.EasyDemo.Website2 @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers3 @inject Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration TelemetryConfiguration

这个是默认情况下帮我们添加的TagHelper

我们可以在要用到那个TagHelper的地方添加就好

@{  Layout = null; }@addTagHelper Catcher.EasyDemo.Controllers.TagHelpers.CatcherATagHelper , Catcher.EasyDemo.Controllers <catcher-a action="list" controller="product" link-text="text">With LinkText And InnerHtml</catcher-a><br /><catcher-a action="list" controller="product" link-text="">Without LinkText</catcher-a> <br /><catcher-a action="list" controller="product" link-text="Only With LinkText"></catcher-a>

addTagHelper的用法如下:

@addTagHelper 你的TagHelper , 你的TagHelper所在的命名空间

或者更直接

@addTagHelper * , 你的TagHelper所在的命名空间

可以添加,当然也可以删除,删除是@removeTagHelper

当我们在自己的框架中完全重写了一套自己的TagHelper,那么这个时候,微软自己的TagHelper我们就可以通过下面的方法来移除了。

@removeTagHelper * , Microsoft.AspNetCore.Mvc.TagHelpers

原文地址:http://www.cnblogs.com/catcher1994/p/5790720.html


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

TagHelper是怎么实现的相关推荐

  1. ASP.NET MVC Core的TagHelper (高级特性)

    ASP.NET MVC Core的TagHelper (高级特性) 转载于:https://www.cnblogs.com/dragon-L/p/8946726.html

  2. MVC6 (ASP.NET5) 自定义TagHelper

    1) 在 _ViewImports.cshtml 中引入TagHelper类所在的 Assembly . (注意不是namespace)  : @addTagHelper "*, WebAp ...

  3. html表格标签高级应用,asp.net core标签助手的高级用法TagHelper+Form

    上一篇博客我讲解了TagHelper的基本用法和自定义标签的生成,那么我就趁热打铁,和大家分享一下TagHelper的高级用法~~,大家也可以在我的博客下随意留言. 对于初步接触asp.net cor ...

  4. WTM系列视频教程:View和Taghelper

    文字摘要: "又到了老刘胡说的时间了,今天我们主要聊一下View和TagHelper.在前后端不分离的模式下,View还是很重的一块." "前几课有朋友反馈说收获很大,也 ...

  5. asp.net core高级应用:TagHelper+Form

    上一篇博客<asp.net core新特性(1):TagHelper>我讲解了TagHelper的基本用法和自定义标签的生成,那么我就趁热打铁,和大家分享一下TagHelper的高级用法~ ...

  6. asp.net core新特性(1):TagHelper

    进步,才是人应该有的现象.-- 雨果 今天开始,我就来说说asp.net core的新特性,今天就说说TagHelper标签助手.虽然学习.net,最有帮助的就是microsoft的官方说明文档了,里 ...

  7. ASP.NET Core MVC TagHelper实践HighchartsNET快速图表控件

    ASP.NET Core MVC TagHelper最佳实践HighchartsNET快速图表控件支持ASP.NET Core. 曾经在WebForms上写过 HighchartsNET快速图表控件- ...

  8. 关于TagHelper的那些事情——自定义TagHelper(内嵌TagHelper)

    内嵌TagHelper 上一篇文章中提到有时候需要设计一种内嵌的TagHelper,如下: <my name="yy" age="35"><l ...

  9. 解读ASP.NET 5 MVC6系列(13):TagHelper

    解读ASP.NET 5 & MVC6系列(13):TagHelper 原文:解读ASP.NET 5 & MVC6系列(13):TagHelper 在新版的MVC6中,微软提供了强大的T ...

最新文章

  1. 如何优雅地拿到30k月薪的offer?这几个大咖就是来帮你搞定薪水和面试官的
  2. 牛客挑战赛51 E NIT的gcd(欧拉反演,建图优化,三元环计数)
  3. SUSE LINUX配置ORACLE命令
  4. 文件流、目录流、文件描述符总结
  5. ElasticSearch和mysql对比
  6. js 执行环境 活动对象 变量对象 作用域链的理解
  7. 阿里面试真题!《阿里云技术面试红宝书》!赶紧保存下载!
  8. 如何用计算机装手机系统,如何使用手机给电脑安装Windows10系统?
  9. win10用什么清理垃圾好?
  10. invalid combination of type specifiers 解决方法
  11. VJ第一周算法题(A - Mud Puddles)
  12. Vue打包出现Browserslist: caniuse-lite is outdated
  13. Error creating bean with name ‘processEngine‘: FactoryBean threw exception on object creation; neste
  14. c语言遍历算法的头文件,图优先遍历算法(C语言版).doc
  15. (淘宝无限适配)移动手机端rem布局详解(转载非原创)
  16. 新浪微博模拟登陆并发文
  17. 网站徽章shields制作教程
  18. [c++][记录]编译libusb-win32过程
  19. Java中的main( )函数
  20. php食堂管理刷卡系统论文,食堂管理系统_毕业设计论文

热门文章

  1. 关于c/c++/obj-c的混合使用 (2010-06-22 10:05:33)
  2. Outlook 阅读窗格(Reading Pane)
  3. .NET 6 使用 Obfuscar 进行代码混淆
  4. Xamarin效果第十四篇之玩耍GIS
  5. 将k8s制作成3D射击游戏,好玩到停不下来,附源码
  6. “快准顺”而不是“信达雅”
  7. VS2022+.NET6+C#10,.NET开发起飞
  8. 如何提高Debug效率
  9. 如何在 ASP.NET Core 中写出更干净的 Controller
  10. 程序员过关斩将--少年派登录安全的奇幻遐想