饮水思源,来自:http://www.cnblogs.com/liontone 的BLOG中关于taghelper中的内容
概要
TagHelper是ASP.NET 5的一个新特性。也许在你还没有听说过它的时候, 它已经在技术人员之间引起了大量讨论,甚至有一部分称它为服务器控件的回归。实际上它只不过是一个简化版本,把HTML和服务器内容混合在一起,没有控件生命周期,状态保持和事件。它不像服务器控件那样,对页面所有内容都具有访问权限。它只能访问到自己所生成的内容。
什么是TagHelper?
我们曾经在MVC项目中使用在HtmlHelps。比如当需要在View上添加一个导航栏的时候,我们就会添加下面代码到页面上去:
1
2
3
4
5
|
<
ol
>
…
<
li
>@Html.ActionLink("Home", "Index", "Home")<
li
>
…
</
ol
>
|
这里有一些HTML元素。还有以@开头的一些C#代码,当View解析的时候这些C#代码会被解析成HTML元素。
当我们用TagHelper,我们同样可以用下面的代码来获得上面同样的效果:
1
2
3
4
5
|
<
ol
>
…
<
li
><
a
controller="Home" action="Index">Home</
a
></
li
>
…
</
ol
>
|
在这里,A元素的属性controller和action并不是HTML5的属性,而是这个TagHelper的属性。
这里需要着重申明一点是,虽然TagHelper看起来有点像我们之前服务器控件的写法,但是它不是服务器控件的再次回归。它更像是给用户一个简洁的方式来表达用户的意向。它本身并没有生命周期之类的东西。它会预处理服务器内容,然后赋值给相应的属性。我们来看一下关于微软提供的SelectTagHelper的一个用法:
1
|
<
select
asp-for="Country" asp-items="ViewBag.Countries">
|
其中文本“Country”被赋给asp-for属性,变量ViewBag.Countries被赋给属性asp-items.
怎么使用TagHelpers?
关于如何使用TagHelpers,我们可以参照一下几步:
-
- 在project.json文件中添加具有TagHelpers的package的依赖;
- 在所用的View上注册TagHelper:
1@addTagHelper “[the full type name of taghelper,] the assembly name”
第一参数是TagHelper类的全名,当你只需要使用某一个TagHelper时候,你可以在此指定你所用的TagHelper类全名,包括它的NameSpace。如果你要使用程序集中所有的TagHelpers,在这里你可以使用“*”或者省略这一个参数。举个例子,如果你需要使用微软提供的TagHelpers可以通过添加下面的代码在注册:
1@addTagHelper
"*, Microsoft.AspNet.Mvc.TagHelpers"
或者
1@addTagHelper
"Microsoft.AspNet.Mvc.TagHelpers"
如果你只希望使用AnchorTagHelper,那么只需要一下代码:
1@addTagHelper
"Microsoft.AspNet.Mvc.TagHelpers.AnchorTagHelper, Microsoft.AspNet.Mvc.TagHelpers"
第二个参数是程序集名。
当然,如果你想取消某个TagHelper的注册,可以使用removeTagHelper,比如1@removeTagHelper
"Microsoft.AspNet.Mvc.TagHelpers.AnchorTagHelper, Microsoft.AspNet.Mvc.TagHelpers"
一旦TagHelper被取消注册了,其对应的Tag就不能被解析成这种TagHelper了。
- 在View中需要使用的地方添加TagHelper,比如我们添加一个AnchorTagHelper:
1<
a
asp-controller="Home" asp-action="About">About</
a
>
微软随着ASP.NET5一起发布的TagHelpers。它们分别是:
- AnchorTagHelper
- CacheTagHelper
- EnvironmentTagHelper
- InputTagHelper
- LabelTagHelper
- SelectTagHelper
- OptionTagHelper
- TextAreaTagHelper
- ValidationMessageTagHelper
- ValidationSummaryTagHelper
- FormTagHelper
- LinkTagHelper
- ScriptTagHelper
AnchorTagHelper
这个TagHelper被应用在所有锚元素<a>上,它拥有一下属性:
- asp-action
指定action方法名。 - asp-controller
指定controller名。 - asp-fragment
指定URL片段名。 - asp-host
指定访问的主机(host)名。 - asp-protocol
指定访问协议,比如http或者https。 - asp-route
指定路由名。
最终这个TagHelper会被解析成具有href属性的锚元素,这个href的内容就是基于以上这些属性的值生成的。
1
|
<
a
asp-action="Create">Create New</
a
>
|
CacheTagHelper
应用在cache元素上,使用IMemoryCache实例来缓存cache元素内容在当前进程的内存中。它支持下面的属性:
- vary-by
string类型,TagHelper将基于该值来缓存内容,该值会被用来生成cache key。 - vary-by-header
string类型,指定请求头(request header),只能指定单个头名(header name),TagHelper将基于该值来缓存内容,该值会被用来生成cache key。 - vary-by-query
string类型,指定请求参数,当有多个请求参数时,以逗号分隔。TagHelper将基于它来缓存内容,该值会被用来生成cache key。 - vary-by-route
string类型,指定路由数据参数,当有多个路由数据参数时,以逗号分隔。TagHelper将基于它来缓存内容,该值会被用来生成cache key。 - vary-by-cookie
string类型,指定cookies名,当有多个cookies时,以逗号分隔。TagHelper将基于这些cookies名来缓存内容,该值会被用来生成cache key。 - vary-by-user
bool类型,指定是否为每个登陆的用户使用缓存,用户信息被用来生成cache key。 - expires-on
DateTime类型,指定缓存失效的时间。 - expires-after
TimeSpan类型,指定经过多少时间,缓存失效,这个时间是从加入缓存开始计时。 - expires-sliding
TimeSpan类型,指定缓存没被使用后经过多少时间失效。 - priority
enum类型,具有以下可能的值:- CachePreservationPriority.Low
- CachePreservationPriority.Normal
- CachePreservationPriority.High
- CachePreservationPriority.NeverRemove
EnvironmentTagHelper
应用在environment元素上,根据不同的names的设置有条件的render不同的内容。它支持以下属性:
- names
指定环境名,当有多个时候以逗号分隔。这里判断的依据是,读取IHostingEnvironment
的EnvironmentName
的值,与environment元素中的names匹配,当匹配上的时候就render出里面的内容,否则移除该environment元素。
在很多情况下,我们想再开发环境使用一套配置信息,在生产环境又是另外一套,这时候就需要使用条件判断语句了,不过在新版的MVC中,使用EnvironmentTagHelper提供的Environment元素标签就可以了,示例如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
<
environment
names="Development">
<
script
src="~/lib/jquery/jquery.js"></
script
>
<
script
src="~/lib/bootstrap/js/bootstrap.js"></
script
>
<
script
src="~/lib/hammer.js/hammer.js"></
script
>
<
script
src="~/lib/bootstrap-touch-carousel/js/bootstrap-touch-carousel.js"></
script
>
</
environment
>
<
environment
names="Staging,Production">
<
script
src="//ajax.aspnetcdn.com/ajax/jquery/jquery-1.10.2.min.js"
asp-fallback-src="~/lib/jquery/jquery.min.js"
asp-fallback-test="window.jQuery">
</
script
>
<
script
src="//ajax.aspnetcdn.com/ajax/bootstrap/3.0.0/bootstrap.min.js"
asp-fallback-src="~/lib/bootstrap/js/bootstrap.min.js"
asp-fallback-test="window.jQuery">
</
script
>
<
script
src="//ajax.aspnetcdn.com/ajax/hammer.js/2.0.4/hammer.min.js"
asp-fallback-src="~/lib/hammer.js/hammer.js"
asp-fallback-test="window.Hammer">
</
script
>
<
script
src="//ajax.aspnetcdn.com/ajax/bootstrap-touch-carousel/0.8.0/js/bootstrap-touch-carousel.js"
asp-fallback-src="~/lib/bootstrap-touch-carousel/js/bootstrap-touch-carousel.js"
asp-fallback-test="window.Zepto">
</
script
>
</
environment
>
|
在上述代码中,我们定于,如果是Development环境就使用本地的js文件,否则(Staging或Production环境)就先加载cdn的文件。
InputTagHelper
这个TagHelper被应用在input元素上,与HtmlHelpers中的TextBoxForHTML一样,这个TagHelper会生成一个绑定到model中某个字段的Input元素。它支持一下属性:
- asp-for
- asp-format
asp-for用来指定绑定model哪个字段到TagHelper上,很多其他的TagHelpers也具有这个属性。
asp-format用来设置显示的Format,通常被用来给货币、日期和时间类型的值设置Format,比如,Birthday是model里的一个日期类型的字段。
1
|
<
input
asp-for="Birthday" asp-format="{0:yyyy-MM-dd}"/>
|
注意: asp-for的类型ModelExpression是ASP.NET MVC 6里面新定义的一个类,其构造函数带有一个字符类型的参数,用来指定Model中字段的名字。我们也可以把一个内嵌对象赋给asp-for,比如:
1
|
<
input
asp-for="Address.Street" type="text" />
|
LabelTagHelper
与HtmlExtension.LabelFor功能一样,它只有一个属性asp-for,用来指定绑定Model里某个字段。它作用在label元素上。
1
|
<
label
asp-for="Birthday"/>
|
SelectTagHelper
SelectTagHelper作用在Select元素上,支持asp-for和asp-items属性。
asp-for与我们上面介绍的一样,用来绑定model中某个字段。
asp-items,被用来指定Select元素的Option集合,它的值类型是IEnumerable<SelectListItem>。
1
|
<
select
asp-for="Country" asp-items="ViewBag.Countries">
|
如果要在Select中添加一个默认选择的项,我们可以这样做:
1
2
3
|
<
select
asp-for="Country" asp-items="ViewBag.Countries">
<
option
selected="selected"value="">Choose Country</
option
>
</
select
>
|
1
2
3
4
5
6
7
8
|
@{
SelectListItem[] items =
{
new SelectListItem() { Text = "item 1" },
new SelectListItem() { Text = "item 2" }
};
}
<
select
asp-for="Country" asp-items="items"></
select
>
|
OptionTagHelper
应用在option元素上,和select元素一起使用,通常被用来读取option元素信息,而不改变元素内容。唯一可能修改的是在有的情况下,会根据父亲select元素将option的selected状态设成"selected"。
1
2
3
|
<
select
asp-for="Country"asp-items="ViewBag.Countries">
<
option
selected="selected" value="">Choose Country</
option
>
</
select
>
|
TextAreaTagHelper
应用在textarea元素上,目前只支持唯一一个属性asp-for,
1
|
<
textarea
asp-for="Information"></
textarea
>
|
ValidationMessageTagHelper
与HtmlHelper中的ValidationMessageFor一样,这个TagHelper是用来显示验证失败信息。它应用在span元素上,而且只有唯一的一个属性asp-validation-for,被用来指定所验证的对象----Model中某个字段。
1
2
|
<
input
asp-for="Birthday" asp-format="{0:yyyy-MM-dd}"/>
<
span
asp-validation-for="Birthday"/>
|
ValidationSummaryTagHelper
像HTMLHelper扩展的ValidationSummary一样,它是用来验证错误的汇总信息。它只支持一个属性asp-validation-summary,具有以下几种值:
- None
不显示任何验证信息 - ModelOnly
只显示Model错误信息,不包括属性错误信息 - All
显示所有信息
1
|
<
div
class="validation" asp-validation-summary="ModelOnly"/>
|
FormTagHelper
与HtmlHelper中的BeginForm一样,它用来生成一个form元素,它应用在form元素上,支持以下属性:
- asp-action
- asp-controller
- asp-anti-forgery
1
|
<
form
asp-action="FormSave" asp-controller="Home" asp-anti-forgery="true">
|
LinkTagHelper
应用在link元素上,支持备用的样式文件。它具有以下属性:
- href
指定样式资源的链接地址。 - asp-href-include
指定所有需要被加载的样式文件路径格式,当有多个时,以逗号来分隔每一个;这里的路径是相对于应用程序中wwwroot的相对路径。 - asp-href-exclude
指定那些不需要被加载的样式文件路径格式,当有多个时,以逗号来分隔每一个;这里的路径是相对于应用程序中wwwroot的相对路径。 - asp-fallback-href
指定备用资源链接地址。 - asp-fallback-href-include
指定所有需要被加载的备用样式文件路径格式,当有多个时,以逗号来分隔每一个;这里的路径是相对于应用程序中wwwroot的相对路径。 - asp-fallback-href-exclude
指定那些不需要被加载的备用样式文件路径格式,当有多个时,以逗号来分隔每一个;这里的路径是相对于应用程序中wwwroot的相对路径。 - asp-fallback-test-class
用来检测加载失败的样式名。 - asp-fallback-test-property
用来检测资源加载失败所用的测试属性。 - asp-fallback-test-value
用来检测资源加载失败所用的测试值。 - asp-file-version
bool值,用来指定是否需要将文件版本信息加入到url地址中。
1
2
3
4
5
6
|
<
link
rel="stylesheet"
href="//ajax.aspnetcdn.com/ajax/bootstrap-touch-carousel/0.8.0/css/bootstrap-touch-carousel.css"
asp-fallback-href="~/lib/bootstrap-touch-carousel/css/bootstrap-touch-carousel.css"
asp-fallback-test-class="carousel-caption"
asp-fallback-test-property="display"
asp-fallback-test-value="none" />
|
ScriptTagHelper
应用在script元素上,和LinkTagHelper一样,它也具有fallback功能, 只不过这里判断的不是class样式,而是检测某个对象是否存在,来判断默认的js文件是否加载成功。- src
指定要加载的js源地址。 - asp-src-include
指定要加载的js文件格式,当有多个文件格式时以逗号分隔。这里文件路径是相对于程序webroot的相对路径。 - asp-src-exclude
指定不需要加载的js文件格式,当有多个文件格式时以逗号分隔。这里文件路径是相对于程序webroot的相对路径。 - asp-fallback-src
指定备用的js源地址。 - asp-fallback-src-include
指定需要加载的备用js文件格式,当有多个文件格式时以逗号分隔。这里文件路径是相对于程序webroot的相对路径。 - asp-fallback-src-exclude
指定不需要加载的备用js文件格式,当有多个文件格式时以逗号分隔。这里文件路径是相对于程序webroot的相对路径。 - asp-fallback-test
指定用来检测js加载成功与否的对象 - asp-file-version
bool值,用来指定是否需要将文件版本信息加入到url地址中。
1
2
3
4
|
<
script
src="//ajax.aspnetcdn.com/ajax/jquery/jquery-1.10.2.min.js"
asp-fallback-src="~/lib/jquery/jquery.min.js"
asp-fallback-test="window.jQuery">
</
script
>
|
前面介绍了TagHelper的基本概念和内嵌的TagHelpers,想必大家对TagHelper都有一定的了解。TagHelper看上去有点像WebControl,但它不同于WebControl,没有复杂的生命周期、状态保持、服务器事件以及较高权限,它只能修改自己Tag的内容。有时觉得它更像angular写出来的一个widget,有自己特有的Tag,并对其进行解析生成出widget ui和启动脚本,但是它具有更高的权限,能访问服务器端信息。
在这章,将要介绍如何自定义TagHelper,考虑到内容比较多,会分成几个小章节来介绍。
TagHelper基类
通过查看内嵌的TagHelpers的源码,发现这些TagHelpers都继承自Microsoft.AspNet.Razor.Runtime.TagHelpers.TagHelper抽象类,而这个类又实现了接口Microsoft.AspNet.Razor.Runtime.TagHelpers.ITagHelper。这个接口是TagHelper最底层的接口,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
/// <summary>
/// Contract used to filter matching HTML elements.
/// </summary>
public
interface
ITagHelper
{
/// <summary>
/// 获取实现ITagHelper接口的类的解析顺序.
/// 值小的先用该类来解析。
/// </summary>
int
Order {
get
; }
/// <summary>
/// 根据所给参数context和output异步解析实现ITagHelper的当前类.
/// </summary>
/// <param name="context">当前HTML Tag的相关信息</param>
/// <param name="output">用于生成HTML Tag的HTML元素.</param>
/// <returns>用于更新output的Task实例.</returns>
Task ProcessAsync(TagHelperContext context, TagHelperOutput output);
}
|
TagHelper类,主要实现了上面接口,添加了一个同步方法来解析当前类,定义如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
/// <summary>
/// Class used to filter matching HTML elements.
/// </summary>
public
abstract
class
TagHelper : ITagHelper
{
public
virtual
int
Order {
get
; } = 0;
//同步解析当前TagHelper
public
virtual
void
Process(TagHelperContext context, TagHelperOutput output)
{
}
//异步解析当前TagHelper
public
virtual
async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
Process(context, output);
}
}
|
一般自定义TagHelper时,只需要设计的类继承于TagHelper类,重载Process方法,根据context,设置output输出html元素和脚本即可。
TagHelper类名
自定义的TagHelper类名格式为***TagHelper,其中省略部分***就是该TagHelper应用的Tag。比如我们定义:
1
2
3
4
|
public
class
MyTagHelper: TagHelper
{
....
}
|
那么页面上所有Tag是my都会进入到这个类来解析,注意Tag名要是小写。
如果***中包括好几个大写字母,那么对于的Tag名是将大写字母变小写前面加上"-",如果大写字母在第一个位置,那么只需要改成小写,前面不需要加“-”。比如
1
2
3
4
|
public
class
MyFirstOneTagHelper: TagHelper
{
...
}
|
那么作用的Tag是
1
|
<
my-first-one
... ></
my-first-one
>
|
我们还可以在类上添加TargetElementAttribute来设置作用的Tag,比如定义如下的类时,其作用的Tag名为my。
1
2
3
4
5
|
[TargetElement(
"my"
)]
public
class
MyFirstOneTagHelper: TagHelper
{
...
}
|
Attributes
在最新的VS2015RC版,开始支持了TagHelper的智能提示,主要体现在在写TagHelper有Attributes的提示,正确的Tag和Attribute会变成粗体,错误的Attribute不会变成粗体,这样就很容易发现书写错误。
-
-
如何定义一个TagHelper的Attribute?
Attribute就是Property。也就是说TagHelper类中的具有getter和setter的公开Property就是它的Attribute。
12345678910111213public
class
MyTagHelper: TagHelper
{
//私有property不是Attribute
private
int
ID {
get
;
set
;}
//这个才会是Attribute<br> public string Name {get; set;}
<br>
//只读的property也不是Attribute<br> public string Sex {get;}
public
override
void
Process(TagHelperContext context, TagHelperOutput output)
{
...
}
}
上面我们定义了MyTagHelper,它只有一个Attribute是Name。所以View上html应该是这样:
1<
my
name="yy"></
my
>
-
Attribute名和Property名关系
-
Attribute名一般都是小写,会将Property名将大写字母变小写前面加上"-",如果大写字母在第一个位置,那么只需要改成小写,前面不需要加“-”。这点和TagHelper的作用的Tag名和TagHelper类之间的关系有些类似。
比如1234public
class
MyTagHelper: TagHelper
{
public
string
ParentName {
get
;
set
;}
}
其对应的View上的html是:
1<
my
parent-name="Mike"></
my
>
-
当然我们也可以在Property头上添加HtmlAttributeName来添加自定义的Attribute名,这样就让这两者之间毫无关系。
比如12345678public
class
MyTagHelper: TagHelper
{
[HtmlAttributeName(
"male"
)]
public
bool
Sex {
get
;
set
;}
[HtmlAttributeName(
"NickName"
)]
public
string
Name {
get
;
set
;}
}
这样MyTagHelper里面的Sex Property对应的Attribute在View上名字是male,而Name Property对应的Attribute是NickName:
1<
my
male="true" NickName="yy"></
my
>
-
-
不想让一个公开的Property成为Attribute(经查证,HtmlAttributeNotBound是在Beta5中才有的。)
有时候某个Property不得不公开出来,但是又不想其作为一个Attribute,怎么办?
最简单的一个方法就是在这个Property上添加HtmlAttributeNotBound,这样就不会生成相应的Attribute了。
123456public
class
MyTagHelper: TagHelper
{
[HtmlAttributeNotBound()]
public
bool
Sex {
get
;
set
;}
public
string
Name {
get
;
set
;}
}
还有一个不推荐的方法就是将setter设成internal或者private。
-
Attribute类型
通常Property的所有类型,Attribute都支持。那么怎么在Tag上设置Attribute的值?- 设置常量(bool, number, string, enum)
1<
my
name="yy" age="34" married="true" sex="Sex.Male"></
my
>
12345public
enum
Sex
{
Male = 0,
FeMale =1
}
- 设置成一个变量
12345@{
var
name =
"yy"
;<br>
var
age = 34;
}
<my name=
"@name"
age=
"age"
></my>
这里有一点需要注意,当Attribute的Type(也就是Property的Type)是string类型时,在设置Attribute值时需要在这个变量前加上@符号,用以和文本常量“name”作区分,其他的类型可以省去这个@符号。
- 设置成一个表达式
1<
my
name="yy" location="new Location{Country="China", City="Shanghai"}"></
my
>
其中location Attribute的Type是Location,定义如下:
12345public
class
Location
{
public
string
Country{
get
;
set
;}
public
string
City{
get
;
set
;}
}
- 设置常量(bool, number, string, enum)
-
Property类型是复杂类型
例如上面location Attribute, 这是一个复杂类型,有公开的构造函数(默认不写构造函数,会创建一个不带参数的构造函数),我们很自然想到上面4里介绍的方法来设置这个Attribute值的几种方式:
- 一就是创建出一个实例出来,然后赋给一个变量,再将这个变量赋给Attribute;
- 另外一种就是直接把创建实例的表达式赋给Attribute。
- 构造函数没有public出来,用户无法创建出这样的实例;
- 类型非常复杂,如果通过code方式创建这样的实例,代码会比较多,而且设置了哪些值会不直观,如果像WebControl那样可以有一个内嵌的Tag来设置会比较直观。
-
关于TagHelper的那些事情——自定义TagHelper(内嵌TagHelper)
内嵌TagHelper
上一篇文章中提到有时候需要设计一种内嵌的TagHelper,如下:
1
2
3
|
<my name=
"yy"
age=
"35"
>
<location country=
"China"
city=
"Shanghai"
district=
"PuDong"
></location>
</my>
|
location就是一个内嵌的TagHelper,我们可以在location里设置与它相关的Attributes,有时候设置有多层内嵌的TagHelper。那么怎样设计出这样的一个内嵌的TagHelper来呢?其实它和一般的TagHelper没什么的区别,大家可以利用前面我们介绍的来设计出它的TagHelper类及Attributes。和一般的TagHelper的主要区别是,它和父TagHelper关联。如何建立这种关联?这也是这章我们需要搞清楚的。
通过前面章节学习,我们知道设计的自定义TagHelper类都会继承于抽象类TagHelper,在这个类中有二个方法:
1
2
3
4
5
|
//同步处理
public
virtual
void
Process(TagHelperContext context, TagHelperOutput output);
//异步处理
public
virtual
Task ProcessAsync(TagHelperContext context, TagHelperOutput output);
|
一般我们会根据实际情况来选择重载哪个方法。在TagHelper抽象类中,异步处理是调用同步处理方法。在这两个方法中参数相同,在这里详细介绍第一个参数context。
它的类型是TagHelperContext,主要是存放TagHelper相关的信息:
-
AllAttributes
TagHelper支持的所有Attribute集合,它是只读的。
-
Items
类型是IDictionary<object, object>,它是用来和其他TagHelper进行联系枢纽,
12345678/// <summary>
/// Gets the collection of items used to communicate with other <see cref="ITagHelper"/>s.
/// </summary>
/// <remarks>
/// This <see cref="IDictionary{object, object}"/> is copy-on-write in order to ensure items added to this
/// collection are visible only to other <see cref="ITagHelper"/>s targeting child elements.
/// </remarks>
public
IDictionary<
object
,
object
> Items {
get
; }
从上面的描述可以看出,这个集合是copy-on-write,也就是说当前TagHelper在Items获取父TagHelper中的Items信息,也可以修改,删除或者添加某一项,但是不会影响到父TagHelper的Items值,同时也会将修改后的Items信息传给其子TagHelper,子TagHelper的任何修改不会影响到它。
在上面我们提到,设计支持内嵌TagHelper类,关键是要建立父子TagHelper的联系,看到这里,我想大家都应该清楚如何建立这种关联。对了,就是利用context中Items。
主要是在TagHelper的Process方法中做以下事情。
1
2
3
4
5
6
7
|
public
virtual
void
Process(TagHelperContext context, TagHelperOutput output)
{
// 从context.items获取父TagHelper信息
// 处理自身Attributes
// 将自身信息存放在context.items中
// 处理自己的子TagHelper
}
|
更具体的例子如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
|
//定义父TagHelper
public
class
PersonTagHelper : TagHelper
{
public
string
Name
{
get
{
return
Person.Name; }
set
{ Person.Name = value; }
}
public
int
Age
{
get
{
return
Person.Age; }
set
{ Person.Age = value; }
}
private
Person _person;
private
Person Person
{
get
{
return
_person ?? (_person =
new
Person());
}
set
{
_person = value;
}
}
public
override
void
Process(TagHelperContext context, TagHelperOutput output)
{
// 保存信息给子TagHelper使用
context.Items[
"Parent"
] = Person;
// 执行并获取子TagHelper内容
context.GetChildContentAsync();
// 输出html元素和启动脚本
// 这里会用到第二个参数output,后面会介绍到如何使用。
}
}
// 定义子TagHelper
public
class
LocationTagHelper: TagHelper
{
public
string
Country {
get
;
set
; }
public
string
City {
get
;
set
; }
public
string
District {
get
;
set
; }
public
override
void
Process(TagHelperContext context, TagHelperOutput output)
{
// 获取来自父TagHelper的信息,并保存到变量里去
var
parent = context.Items[
"Parent"
]
as
Person;
// 处理Attributes的设置
parent.Location.Country = Country;
parent.Location.City = City;
parent.Location.District = District;
// 保存自身信息,便于子TagHelper使用
context.Items[
"Parent"
] = parent.Location;
// 执行并获取子TagHelper内容
context.GetChildContentAsync();
}
}
// 定义了父TagHelper中对应的对象实例
// 大家可以根据各自需求,决定是否需要定义这样一个类型
public
class
Person
{
public
string
Name {
get
;
set
; }
public
int
Age {
get
;
set
; }
private
Location _location;
public
Location Location
{
get
{
return
_location ?? (_location =
new
Location()); }
}
}
// 定义子TagHelper中数据类型
// 大家可以根据各自需求,决定是否需要定义这样一个类型
public
class
Location
{
public
string
Country {
get
;
set
; }
public
string
City {
get
;
set
; }
public
string
District {
get
;
set
; }
}
|
对于类Person和Location的定义大家可以根据具体情况来决定是否需要。
这个例子只是简单展示如何利用Items信息来构建父子TagHelper间的联系,大家在具体的项目开发中根据实际需求,写出不一样的代码来,但有一点不变,就是要利用Items。
关于TagHelper的那些事情——自定义TagHelper(格式化输出、依赖注入使用)
自定义TagHelper的最后一步就是在Process方法或ProcessAsync方法中添加展现代码。熟悉WebControl开发的朋友都知道Render方法,在这个方法中会添加展现的Html元素和启动脚本,TagHelper的这一步我们要做的也就是和Render方法一样。
这里我们主要利用上面方法中的第二个参数output来往View上输出展现部分。
首先让我们看以output类型TagHelperOutput的定义:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
|
/// <summary>
/// Class used to represent the output of an <see cref="ITagHelper"/>.
/// </summary>
public
class
TagHelperOutput
{
/// <summary>
/// Instantiates a new instance of <see cref="TagHelperOutput"/>.
/// </summary>
/// <param name="tagName">The HTML element's tag name.</param>
/// <param name="attributes">The HTML attributes.</param>
public
TagHelperOutput(
string
tagName, IDictionary<
string
,
object
> attributes)
{
...
}
/// <summary>
/// The HTML element's tag name.
/// </summary>
/// <remarks>
/// A whitespace or <c>null</c> value results in no start or end tag being rendered.
/// </remarks>
public
string
TagName {
get
;
set
; }
/// <summary>
/// The HTML element's pre content.
/// </summary>
/// <remarks>Value is prepended to the <see cref="ITagHelper"/>'s final output.</remarks>
public
TagHelperContent PreContent{
get
; }
/// <summary>
/// The HTML element's main content.
/// </summary>
/// <remarks>Value occurs in the <see cref="ITagHelper"/>'s final output after <see cref="PreContent"/> and
/// before <see cref="PostContent"/></remarks>
public
TagHelperContent Content {
get
; }
/// <summary>
/// The HTML element's post content.
/// </summary>
/// <remarks>Value is appended to the <see cref="ITagHelper"/>'s final output.</remarks>
public
TagHelperContent PostContent {
get
; }
/// <summary>
/// <c>true</c> if <see cref="Content"/> has been set, <c>false</c> otherwise.
/// </summary>
public
bool
IsContentModified {
get
; }
/// <summary>
/// Indicates whether or not the tag is self-closing.
/// </summary>
public
bool
SelfClosing {
get
;
set
; }
/// <summary>
/// The HTML element's attributes.
/// </summary>
/// <remarks>
/// MVC will HTML encode <see cref="string"/> values when generating the start tag. It will not HTML encode
/// a <c>Microsoft.AspNet.Mvc.Rendering.HtmlString</c> instance. MVC converts most other types to a
/// <see cref="string"/>, then HTML encodes the result.
/// </remarks>
public
IDictionary<
string
,
object
> Attributes {
get
; }
/// <summary>
/// Changes <see cref="TagHelperOutput"/> to generate nothing.
/// </summary>
/// <remarks>
/// Sets <see cref="TagName"/> to <c>null</c>, and clears <see cref="PreContent"/>, <see cref="Content"/>,
/// and <see cref="PostContent"/> to suppress output.
/// </remarks>
public
void
SuppressOutput()
{
...
}
}
|
TagName:
指定输出到View上最外层Html元素的Tag。
PreContent
指定添加到Html元素主内容(Content)前面的。
Content
指定Html元素的主内容,在PreContent后面,PostContent前面。
PostContent
指定Html元素主内容后面的。
SupressOutput
不生成任何展示内容。
通常我们会根据实际需要设置output中这些属性,其中用的比较多的就是TagName和Content,在TagName指定生成HTML元素的最外层Tag,在Content添加其内部的Html元素和启动脚本。
我们知道ASP.NET 5实现了依赖注入,在TagHelper类中我们也可以通过依赖注入获取更多的系统实例对象,为具体需求所有。我们只需要在TagHelper类中,添加一个相关类型的属性,然后在属性头上添加Activate属性即可自动获取对应实例。比如,获取ViewContext信息,可以在类中添加如下代码:
1
2
|
[Activate]
public
ViewContext ViewContext {
get
;
set
; }
|
这样我们就可以在其他地方,通过属性ViewContext获取当前View的上下文信息。
通过这种方式,你可以获取到更多的系统实例对象,如ActionContext
、HttpContext
、HttpRequest
、HttpResponse
、 ViewDataDictionary
以及ActionBindingContext等