必看
从 ASP.NET MVC 6 开始,Tag Helper 就逐步取代了 HTML Helper。相较于后者,Tag Helper 的可控性更强,更适应现代网页设计开发。
注:虽然 HTML Helper 用得少了,但这种组件封装的思想还是值得一看的。
概述
本节将详细介绍 HTML Helper(HTML 帮助程序)。
本节内容包括:
HTML Helper 的概念
自定义 HTML Helper
内置的 HTML Helper
HTML Helper 和模型
HTML Helper 的概念
我们可以将 Razor 表达式看作 HTML 文本模板上的“窟窿”,这些“窟窿”最终将由 Razor 表达式的值填充。
例如,下面的代码中有四个 Razor 表达式(窟窿):
<td>@p.ID</td><td>@p.Name</td><td>@p.Price</td><td>@(p.Price * rate)</td>,
最终这些 Razor 表达式的“窟窿”将由它们的值填充,大致如下:
<td>101</td><td>Bike</td><td>100</td><td>75</td>.
得到上面 HTML 的原因是这些 Razor 表达式的值是纯文本。但是如果 Razor 表达式的值本身就是 HTML 标签将会怎样?
<h1>Welcome to Seattle</h1>
这将实现将新的 HTML 标签(此处的 h1)嵌入到现有的 HTML 标签中。这便是 HTML Helper 的基本思想。
自定义 HTML Helper
通过自定义 HTML Helper,提前向你揭示它的运行机制。
HTML Helper 通过扩展方法实现。下面这段代码就是我们的自定义 HTML Helper,理论上这段代码放在项目中任何地方都会生效,但推荐你将它放在项目根目录下的 Extensions 文件夹内。
namespace Microsoft.AspNetCore.Mvc.Rendering
{
public static class MyHtmlHelperExtensions
{
public static IHtmlContent ColorfulHeading(this IHtmlHelper htmlHelper, int level, string color, string content)
{
level = level < 1 ? 1 : level;
level = level > 6 ? 6 : level;
var tagName = $"h{level}";
var tagBuilder = new TagBuilder(tagName);
tagBuilder.Attributes.Add("style", $"color:{color ?? "green"}");
tagBuilder.InnerHtml.Append(content ?? string.Empty);
return tagBuilder;
}
}
}
仔细分析这段代码:
我们将该类放在 Microsoft.AspNetCore.Mvc.Rendering 命名空间中。因为 Razor 引擎总是从这个命名空间加载扩展
- 你也可以将该类放在其他命名空间中,但你必须在视图的顶部使用 @use 指令导入命名空间
扩展方法类必须声明为 static
扩展方法也必须是 static 的。返回类型是由 TagBuilder 类实现的 IHtmlContent。
this IHtmlHelper htmlHelper
参数表明这是一个扩展方法方法名称 ColorfulHeading 反映了它的逻辑 —— 为给定的文本内容创建 header 标签,并允许开发人员设置标题级别和前景色
测试代码:
@Html.ColorfulHeading(1, "green", "Welcome to Seattle")
@Html.ColorfulHeading(2, "orange", "Especially in Summer")
效果:
生成的 HTML 源码:
<h1 style="color:green">Welcome to Seattle</h1>
<h2 style="color:orange">Especially in Summer</h2>
用于模型绑定的 HTML Helper
我们知道模型绑定时,一旦从 Web 端发送过来的键值对与模型名或模型的属性名不匹配,模型绑定就会失败。
幸运的是,对于设计良好的 Web 程序,我们始终在领域模型、视图模型和数据传输模型(DTO)之间共享相同的名称集。这使我们有机会通过 Lambda 表达式来约束名称。
如何通过变量获取属性名
在创建受模型名或模型属性名约束的 HTML Helper 程序前,让我们先看看“如何通过 Lambda 表达式获取模型属性名”。下面的示例运行在一个控制台程序中。
首先,准备一个模型类:
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
然后,准备一组泛型类。这组泛型类大致反映了实际视图类的结构。FakeViewPage
public class FakeHtmlHelper<TModel>
{
}
public class FakeViewPage<TModel>
{
public FakeHtmlHelper<TModel> HtmlHelper { get; }
public FakeViewPage()
{
HtmlHelper = new FakeHtmlHelper<TModel>();
}
}
接着,给 FakeHtmlHelper
public static class FakeHtmlHelperExtension
{
public static string GetName<TModel, TResult>(this FakeHtmlHelper<TModel> target, Expression<Func<TModel, TResult>> expression)
{
var body = expression.Body as MemberExpression;
return body.Member.Name;
}
}
最后,我们完善 Main 方法:
static void Main(string[] args)
{
var view = new FakeViewPage<Person>();
var propertyName = view.HtmlHelper.GetName(p => p.ID);
Console.WriteLine(propertyName);
propertyName = view.HtmlHelper.GetName(p => p.Name);
Console.WriteLine(propertyName);
propertyName = view.HtmlHelper.GetName(p => p.Age);
Console.WriteLine(propertyName);
}
运行程序,输出:
ID
Name
Age
约束 HTML 标签的属性
现在就将名称约束应用到 HTML Helper 上。
在 MyHtmlHelperExtensions 类里面添加一个扩展方法:
namespace Microsoft.AspNetCore.Mvc.Rendering
{
public static class MyHtmlHelperExtensions
{
public static IHtmlContent MyTextBoxFor<TModel, TResult>(this IHtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TResult>> expression)
{
var body = expression.Body as MemberExpression;
var propertyName = body.Member.Name.ToLower(); // keep lower case
var tagBuilder = new TagBuilder("input");
tagBuilder.Attributes.Add("type", "text"); // can be omitted
tagBuilder.Attributes.Add("name", propertyName); // key purpose 1: for name-value pair
tagBuilder.Attributes.Add("id", propertyName); // key purpose 2: for client JavaScript and CSS
return tagBuilder;
}
}
}
添加 View 测试该方法:
@model HelloMVC.Models.Person
<html>
<head>
<title></title>
</head>
<body>
@Html.MyTextBoxFor(p => p.ID)<br />
@Html.MyTextBoxFor(p => p.Name)<br />
@Html.MyTextBoxFor(p => p.Age)<br />
</body>
</html>
页面效果:
源码:
<input id="id" name="id" type="text"></input><br />
<input id="name" name="name" type="text"></input><br />
<input id="age" name="age" type="text"></input><br />
显然,我们成功的通过 Lambda 表达式实现了 —— 使用模型的属性名约束生成的 HTML 标签的属性。
这样做得好处是模型绑定的键值对永远不会出错,特别是模型的属性名被更改时。
PS:如果我们在 HTML Helper 扩展方法中创建更复杂的 TagBuilder 结构,我们可以获得如日历、数据表、图表和仪表板等更复杂的 UI 组件。这也是 Telerik UI for ASP.NET MVC 等第三方 HTML Helper 的工作模式。
ASP.NET Core 内置的 HTML
略