这篇博文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 (高级特性)的更多相关文章

  1. ASP.NET MVC Core的TagHelper(基础篇)

    TagHelper又是一个新的名词,它替代了自之前MVC版本的HtmlHelper,专注于在cshmlt中辅助生成html标记. 通过使用自定义的TagHelper可以提供自定义的Html属性或元素, ...

  2. 简述C#中IO的应用 RabbitMQ安装笔记 一次线上问题引发的对于C#中相等判断的思考 ef和mysql使用(一) ASP.NET/MVC/Core的HTTP请求流程

    简述C#中IO的应用   在.NET Framework 中. System.IO 命名空间主要包含基于文件(和基于内存)的输入输出(I/O)服务的相关基础类库.和其他命名空间一样. System.I ...

  3. 为ASP.NET MVC应用程序使用高级功能

    为ASP.NET MVC应用程序使用高级功能 这是微软官方教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻译, ...

  4. .NET CORE学习笔记系列(1)——ASP.NET MVC Core 介绍和项目解读

    ASP.NET MVC Core 项目文件夹解读 一.项目文件夹总览 1.1.Properties——launchSettings.json 启动配置文件,你可以在项目中“Properties”文件夹 ...

  5. 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    将 ...

  6. [转帖]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 ...

  7. ASP.NET MVC Core Starter Kit

    上一篇博文<创建.NET Core程序的Nuget Package>提到准备创建一个Nuget包,用于自动生成一个简单的ASP.NET MVC Core的示例项目.本来是打算用Nuget实 ...

  8. ASP.NET MVC/Core表单提交后台模型二级属性验证问题

    起因 这个是网友在官网论坛的提问:https://fineui.com/bbs/forum.php?mod=viewthread&tid=22237 重新问题 本着务实求真的态度,我们先来复现 ...

  9. ASP.NET/MVC/Core的HTTP请求流程

    ASP.NET HTTP管道(Pipeline)模型 1. 先讲一点,再深刻思考 一般我们都在写业务代码,优化页面,优化逻辑之间内徘徊.也许我们懂得HTTP,HTTPS的GET,POST,但是我们大部 ...

随机推荐

  1. 黄聪:Navicat for MySQL的1577错误解决

    今天尝试使用了Windows下的可视化mysql数据库管理工具Navicat,界面清爽,易操作上手,感觉还不错. 不过当连接上mysql后,无论打开任何一个数据库,都会提示:1577 – Cannot ...

  2. C#两个日期范围内的间隔

    http://www.cnblogs.com/love_study/archive/2011/04/02/2003045.html 引用地址 1 /// <summary> /// 计算日 ...

  3. Java 迭代器 Iterator

    迭代器模式 迭代器模式(Iterator Pattern)是 Java 和 .Net 编程环境中非常常用的设计模式.这种模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示. 迭代器模式属于行 ...

  4. 视频描述(Video Captioning)调研

    Video Analysis 相关领域介绍之Video Captioning(视频to文字描述)http://blog.csdn.net/wzmsltw/article/details/7119238 ...

  5. 【DataGuard】部署Data Guard相关参数详解 (转载)

    原文地址:[DataGuard]部署Data Guard相关参数详解 作者:secooler    有关物理Data Guard部署参考<[DataGuard]同一台主机实现物理Data Gua ...

  6. [Delphi] 调用ocx

    function RegisterDllServer(FileName: string): boolean; var nDllAddr: integer; bstr: string; ProcAddr ...

  7. Java 目标

    Java 技术 其次掌握的技能树主要有三个方面:第一个是基础,比如对集合类,并发包,IO/NIO,JVM,内存模型,泛型,异常,反射,等有深入了解,最好是看过源码了解底层的设计.比如一般面试都会问Co ...

  8. quicker+.em SourceInsight 宏加强版制作

    这两天苦于新建的文件每次都要手动添加文件头,新建的函数每次都要手动添加函数说明,连.h也要手动生成.于是乎,上网搜了搜,发现了lushengwen写的一个神器:quicker.em .好家伙,有了这个 ...

  9. aws代理

    ssh -i ~/.ssh/seoul-notification-dev.pem  ec2-user@52.79.58.125ssh -CqTnN -D localhost:7080   -i ~/. ...

  10. Git报错的解决方案汇总

    错误1: error: Your local changes to the following files would be overwritten by merge:Please, commit y ...