ASP.NET MVC Core的TagHelper (高级特性)
这篇博文ASP.NET MVC Core的TagHelper(基础篇)介绍了TagHelper的基本概念和创建自定义TagHelper的方式,接着继续介绍一些新的看起来比较高级的特性。(示例代码紧接着上一遍博文)
一、使用自定义的标记元素
之前基础篇介绍的TagHelper的功能是给已有的HTML元素提供一个自定义的属性标记,然后服务器认出这个标记后,将标记转化成最终的HTML。这里将要介绍的功能是,定义个全新的Tag,看起来跟普通的HTML元素一样。是不是觉得很熟悉呢(前提是你用过AngularJS),完全类似于AngularJS的强大的元素定义功能。
比如我们这里创建一个新的标记元素,formbutton,使用方式如下
<formbutton type="submit" bg-color="danger" />
当然这个标记完全不是HTML内部定义的,浏览器也不能认出这是个啥玩意。
这个Tag跟自定义的属性标记一样,都会被MVC Core框架识别出来,然后转化成最终的HTML。
接下来我们创建这个TagHelper
在TagHelpers文件夹新建一个类
[HtmlTargetElement("formbutton")]
public class FormButtonTagHelper : TagHelper
{
public string Type { get; set; } = "Submit"; public string BgColor { get; set; } = "primary"; public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.TagName = "button";
output.TagMode = TagMode.StartTagAndEndTag;
output.Attributes.SetAttribute("class", $"btn btn-{BgColor}");
output.Attributes.SetAttribute("type", Type);
output.Content.SetContent(Type == "submit" ? "Add" : "Reset");
}
}
这个class定义的两个属性Type和BgColor,如大部分的猜想,这两个属性会匹配成html中定义的属性,然后把值自动赋给TagHelper Instance中的属性。
Process一连串的output调用也比较直接,大概意思是要生成一个button元素,并且根据用户提供的Type和BgColor生成class和type两个html属性的值。
其中SetContent是要设置需要输出的内容,由于TagMode是StartTagAndEndTag,所以内容会显示在标记之间。
接下来在home/create这个页面使用我们的自定义标记
@model City
@{ Layout = "_Layout"; }
<form method="post" action="/Home/Create">
<div class="form-group">
<label for="Name">Name:</label>
<input class="form-control" name="Name" />
</div>
<div class="form-group">
<label for="Country">Country:</label>
<input class="form-control" name="Country" />
</div>
<div class="form-group">
<label for="Population">Population:</label>
<input class="form-control" name="Population" />
</div>
<formbutton type="submit" bg-color="danger" />
<formbutton type="reset" />
<a bs-button-color="primary" href="/Home/Index">Cancel</a>
</form>
我们使用formbuttion分别创建了一个submit和reset按钮,并且给submit按钮设置了danger样式
那么这两个按钮输出后的html分别是
<button class="btn btn-danger" type="submit">Add</button>
<button class="btn btn-primary" type="reset">Reset</button>
这是创建一个基本的自定义TagHelper的使用方式。
二、在目标元素之前或者之后插入内容
上一个栗子,比较中规中矩,实际上我们经常需要给元素前后插入一些内容,通常是一些外围包含元素。比如有如下元素
<div title="Cities"></div>
我们希望这个标记在输出成html的时候能在前后都自动加上一个div class=panel-body的色块,那么我么可以利用TagHelperOutput提供的方法实现。
可以创建如下的自定义TagHelper来说明
在TagHelpers文件夹新建类ContentWrapperTagHelper
[HtmlTargetElement("div", Attributes = "title")]
public class ContentWrapperTagHelper : TagHelper
{
public bool IncludeHeader { get; set; } = true;
public bool IncludeFooter { get; set; } = true; public string Title { get; set; } public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.Attributes.SetAttribute("class", "panel-body"); var title = new TagBuilder("h1");
title.InnerHtml.Append(Title); var container = new TagBuilder("div");
container.Attributes["class"] = "bg-info panel-body";
container.InnerHtml.AppendHtml(title); if (IncludeHeader)
{
output.PreElement.SetHtmlContent(container);
} if (IncludeFooter)
{
output.PostElement.SetHtmlContent(container);
}
}
}
1.这里指定了TagHelper的应用范围是包含了title属性的div元素
2.分别提供了IncludeHeader和IncludeFooter的属性,默认都是true
3.然后分别使用PreElement和PostElement设置前后内容
我们把这个标签应用在_Layout文件中
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Cities</title>
<link href="/lib/bootstrap/dist/css/bootstrap.css" rel="stylesheet" />
</head>
<body class="panel-body">
<div title="Cities">@RenderBody()</div>
</body>
</html>
运行就能看到头部和底部分别都输出了一个色块,并且包含了标题内容
三、在已有标记内容中插入内容
上一个栗子讲的是插入元素,这里演示一下插入内容到标签中,比如已有标签里面已经有内容了,可以在内容之前或者之后插入内容。
在TagHelpers目录新建一个TableCellTagHelper类
[HtmlTargetElement("td", Attributes = "wrap")]
public class TableCellTagHelper : TagHelper
{
public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.PreContent.SetHtmlContent("<b><i>");
output.PostContent.SetHtmlContent("</i></b>");
}
}
通过使用TagHelperOutput的PreContent和PostContent,分别在已有内容的前后插入了一段html标记包裹,这个TagHelper只会用于带有wrap属性的td标记。
把这个标记用在Home/Index.cshtml页面,把city的名称的td加入wrap属性即可
@model IEnumerable<City>
@{ Layout = "_Layout"; }
<table class="table table-condensed table-bordered">
<thead class="bg-primary">
<tr>
<th>Name</th>
<th>Country</th>
<th class="text-right">Population</th>
</tr>
</thead>
<tbody>
@foreach (var city in Model)
{
<tr>
<td wrap>@city.Name</td>
<td>@city.Country</td>
<td class="text-right">@city.Population?.ToString("#,###")</td>
</tr>
}
</tbody>
</table>
<a href="/Home/Create" class="btn btn-primary">Create</a>
运行后可以看到已有的内容都被<i><b></b></i>包裹起来,呈现的是加粗和斜体的效果。
四、使用ViewModel提供的属性值
在VIew里面输出ViewModel的值,经常会用到一些强类型的帮助方法,比如asp-for="Name"等,那么实际上就会读取ViewModel的Name的属性值。
自定义的TagHelper也支持这种方式,我们来看一下如何调用,还是继续在TagHelpers目录新建一个类,如下
LabelAndInputTagHelper
[HtmlTargetElement("label", Attributes = "helper-for")]
[HtmlTargetElement("input", Attributes = "helper-for")]
public class LabelAndInputTagHelper : TagHelper
{
public ModelExpression HelperFor { get; set; } public override void Process(TagHelperContext context, TagHelperOutput output)
{
if (output.TagName == "label")
{
output.TagMode = TagMode.StartTagAndEndTag;
output.Content.Append(HelperFor.Name);
output.Attributes.SetAttribute("for", HelperFor.Name);
} else if (output.TagName == "input")
{
output.TagMode = TagMode.SelfClosing;
output.Attributes.SetAttribute("name", HelperFor.Name);
output.Attributes.SetAttribute("class", "form-control");
if (HelperFor.Metadata.ModelType == typeof(int?))
{
output.Attributes.SetAttribute("type", "number");
}
}
}
}
这个TagHelper的作用用,将for属性应用到label和input元素上,实现常见的点击label后聚焦到input的功能。
这里一个关键属性是HelperFor,用来读取ViewModel提供的属性的信息,它的类型是ModelExpression,看起来比较高级,用它可以很方便得到ViewModel的信息。
我们把这个TagHelper应用到Home/Create.cshtml页面中
比如之前我们是这样写的
<label for="Name">Name:</label>
<input class="form-control" name="Name" />
现在用了标记之后就可以简化成如下
<label helper-for="Name"/>
<input helper-for="Name"/>
看起来更加的整洁,和符合强迫症程序员的口味。
五. TagHelper之前相互通讯协同
两个不同的TagHelper之前实际上可以通过共享数据的方式实现协同,当然共享数据的方式很多啊,比如粗暴一点的用数据,什么Session之类的(经常面试被问到的Asp.net页面传递有哪些方法啊,通常是老家伙装13的样子在问)
当然我们不会用数据或者Session去保存共享的数据,TagHelperContext为我们提供了一个便利的实现方式,类似于HttpContent.Items,直接看看例子。
在TagHelpers文件夹新建一个CoordinatingTagHelpers文件
[HtmlTargetElement("div", Attributes = "theme")]
public class ButtonGroupThemeTagHelper : TagHelper
{
public string Theme { get; set; } public override void Process(TagHelperContext context,
TagHelperOutput output)
{
context.Items["theme"] = Theme;
}
} [HtmlTargetElement("button", ParentTag = "div")]
[HtmlTargetElement("a", ParentTag = "div")]
public class ButtonThemeTagHelper : TagHelper
{
public override void Process(TagHelperContext context,
TagHelperOutput output)
{
if (context.Items.ContainsKey("theme"))
output.Attributes.SetAttribute("class",
$"btn btn-{context.Items["theme"]}");
}
}
这个文件包含两个TagHelper,第一个是定义了div标签,它在context.Items里设置了theme的值,然后在另外一个TagHelper中读取items的值。
用法简单到没有朋友
<div theme="primary">
<button type="submit">Add</button>
<button type="reset">Reset</button>
<a href="/Home/Index">Cancel</a>
</div>
里面的button的样式会根据外层theme的值来设置对应的样式,比如设置theme="Danger",里面的按钮显示为如下样式
六. 禁止内容输出
最后要介绍的是禁止内容输出。禁止内容输出很多方法,最简单的不显示或者加个if else判断。
这里使用TagHelperOutput提供的SuppressOutput方法。
新建如下TagHelper
[HtmlTargetElement(Attributes = "show-for-action")]
public class SelectiveTagHelper : TagHelper
{
public string ShowForAction { get; set; }
[ViewContext]
[HtmlAttributeNotBound]
public ViewContext ViewContext { get; set; }
public override void Process(TagHelperContext context,
TagHelperOutput output)
{
if (!ViewContext.RouteData.Values["action"].ToString()
.Equals(ShowForAction, StringComparison.OrdinalIgnoreCase))
{
output.SuppressOutput();
}
}
}
这个TagHelper定义了其标签内容只有在当前Action跟目标Action一致的时候在显示内容,否则调用Suppress禁止内容输出
比如如下html标记
<div show-for-action="Index" class="panel-body bg-danger">
<h2>Important Message</h2>
</div>
指定了只有在Index action下才显示important Message
示例代码路径
https://github.com/shenba2014/AspDotNetCoreMvcExamples/tree/master/CustomTagHelper
ASP.NET MVC Core的TagHelper (高级特性)的更多相关文章
- ASP.NET MVC Core的TagHelper(基础篇)
TagHelper又是一个新的名词,它替代了自之前MVC版本的HtmlHelper,专注于在cshmlt中辅助生成html标记. 通过使用自定义的TagHelper可以提供自定义的Html属性或元素, ...
- 简述C#中IO的应用 RabbitMQ安装笔记 一次线上问题引发的对于C#中相等判断的思考 ef和mysql使用(一) ASP.NET/MVC/Core的HTTP请求流程
简述C#中IO的应用 在.NET Framework 中. System.IO 命名空间主要包含基于文件(和基于内存)的输入输出(I/O)服务的相关基础类库.和其他命名空间一样. System.I ...
- 为ASP.NET MVC应用程序使用高级功能
为ASP.NET MVC应用程序使用高级功能 这是微软官方教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻译, ...
- .NET CORE学习笔记系列(1)——ASP.NET MVC Core 介绍和项目解读
ASP.NET MVC Core 项目文件夹解读 一.项目文件夹总览 1.1.Properties——launchSettings.json 启动配置文件,你可以在项目中“Properties”文件夹 ...
- ASP.NET Core 1.0、ASP.NET MVC Core 1.0和Entity Framework Core 1.0
ASP.NET 5.0 将改名为 ASP.NET Core 1.0 ASP.NET MVC 6 将改名为 ASP.NET MVC Core 1.0 Entity Framework 7.0 将 ...
- [转帖]2016年时的新闻:ASP.NET Core 1.0、ASP.NET MVC Core 1.0和Entity Framework Core 1.0
ASP.NET Core 1.0.ASP.NET MVC Core 1.0和Entity Framework Core 1.0 http://www.cnblogs.com/webapi/p/5673 ...
- ASP.NET MVC Core Starter Kit
上一篇博文<创建.NET Core程序的Nuget Package>提到准备创建一个Nuget包,用于自动生成一个简单的ASP.NET MVC Core的示例项目.本来是打算用Nuget实 ...
- ASP.NET MVC/Core表单提交后台模型二级属性验证问题
起因 这个是网友在官网论坛的提问:https://fineui.com/bbs/forum.php?mod=viewthread&tid=22237 重新问题 本着务实求真的态度,我们先来复现 ...
- ASP.NET/MVC/Core的HTTP请求流程
ASP.NET HTTP管道(Pipeline)模型 1. 先讲一点,再深刻思考 一般我们都在写业务代码,优化页面,优化逻辑之间内徘徊.也许我们懂得HTTP,HTTPS的GET,POST,但是我们大部 ...
随机推荐
- 黄聪:Navicat for MySQL的1577错误解决
今天尝试使用了Windows下的可视化mysql数据库管理工具Navicat,界面清爽,易操作上手,感觉还不错. 不过当连接上mysql后,无论打开任何一个数据库,都会提示:1577 – Cannot ...
- C#两个日期范围内的间隔
http://www.cnblogs.com/love_study/archive/2011/04/02/2003045.html 引用地址 1 /// <summary> /// 计算日 ...
- Java 迭代器 Iterator
迭代器模式 迭代器模式(Iterator Pattern)是 Java 和 .Net 编程环境中非常常用的设计模式.这种模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示. 迭代器模式属于行 ...
- 视频描述(Video Captioning)调研
Video Analysis 相关领域介绍之Video Captioning(视频to文字描述)http://blog.csdn.net/wzmsltw/article/details/7119238 ...
- 【DataGuard】部署Data Guard相关参数详解 (转载)
原文地址:[DataGuard]部署Data Guard相关参数详解 作者:secooler 有关物理Data Guard部署参考<[DataGuard]同一台主机实现物理Data Gua ...
- [Delphi] 调用ocx
function RegisterDllServer(FileName: string): boolean; var nDllAddr: integer; bstr: string; ProcAddr ...
- Java 目标
Java 技术 其次掌握的技能树主要有三个方面:第一个是基础,比如对集合类,并发包,IO/NIO,JVM,内存模型,泛型,异常,反射,等有深入了解,最好是看过源码了解底层的设计.比如一般面试都会问Co ...
- quicker+.em SourceInsight 宏加强版制作
这两天苦于新建的文件每次都要手动添加文件头,新建的函数每次都要手动添加函数说明,连.h也要手动生成.于是乎,上网搜了搜,发现了lushengwen写的一个神器:quicker.em .好家伙,有了这个 ...
- aws代理
ssh -i ~/.ssh/seoul-notification-dev.pem ec2-user@52.79.58.125ssh -CqTnN -D localhost:7080 -i ~/. ...
- Git报错的解决方案汇总
错误1: error: Your local changes to the following files would be overwritten by merge:Please, commit y ...