【发布时间】:2017-06-21 17:04:40
【问题描述】:
我想创建我自己的自定义 HTML Helper,就像在 ASP.NET MVC 中使用的那些一样,但我无法找到如何以正确的方式实现它们。
我发现了如何创建自定义标签助手,但不是 HTML 助手。如何创建自己的自定义 HTML 助手?
【问题讨论】:
标签: asp.net-core asp.net-core-mvc
我想创建我自己的自定义 HTML Helper,就像在 ASP.NET MVC 中使用的那些一样,但我无法找到如何以正确的方式实现它们。
我发现了如何创建自定义标签助手,但不是 HTML 助手。如何创建自己的自定义 HTML 助手?
【问题讨论】:
标签: asp.net-core asp.net-core-mvc
Danny van der Kraan 在他的blog post here 中对此进行了很好的解释。下面的答案是这篇文章的摘录:
ASP.NET Core 1.0 [MVC 6] 带有一个令人兴奋的新功能,称为TagHelpers。在ASP.Net Core 1.0 中没有像 MVC 中那样的 HTML Helper 的概念。
什么是 TagHelper?
TagHelpers 可以看作是随着第一个 MVC 框架的推出而引入的 HTML 助手的演变。为了提供上下文,您必须想象使用经典 ASP,您可以自动生成 HTML 的唯一方法是通过自定义子例程。在那之后,ASP.NET 附带了服务器控件,其中视图状态是最大的优势,以模拟桌面应用程序的外观和感觉并帮助桌面开发人员进行过渡。但是我们都知道当我们试图将方块塞进圆孔时会发生什么。我们不得不面对这样一个事实,即 Web 开发与桌面开发完全不同。为了与正确的 Web 开发保持一致,ASP.NET MVC 框架与 HTML 帮助程序一起启动,以自动化 HTML 输出。但是 HTML 助手从来没有真正凝聚在一起,尤其是前端开发人员和设计人员。主要的烦恼之一是,它让你在视图工作期间从尖括号(HTML, CSS) 切换到C# (Razor syntax),这让体验不必要地不舒服。 [MVC 6] 想通过引入 TagHelpers 来解决这个问题和一些较小的问题。例子
HTML 助手:
@Html.ActionLink(”Home”, ”Index”, ”Home”)
使用锚 TagHelper 如下所示:
<a asp-action="Index" asp-controller="Home">Home</a>
PS: 请注意 asp- 只是一个约定,稍后会详细介绍。
两者在浏览器中呈现的输出是相同的:
<a href="/">Home</a>
PS: 前提是默认路由没有改变。
更多关于TagHelpersclick here的信息
【讨论】:
TagHelper 是ASP.Net core 的改进功能。您可以使用TagHelper 完成所有操作。但你究竟是想用一些例子来解释?
HTML Helpers 看起来在 ASP.NET Core 中受支持,正在等待文档:
https://docs.microsoft.com/en-au/aspnet/core/mvc/views/html-helpers
[编辑:]自从回答后,上面的页面不再存在。我想说的是 HTML Helpers,虽然它们工作,但在 ASP.NET Core 中不再“支持”。
查看 ASP.NET Core 源代码,它们的工作方式与旧版本的 ASP.NET MVC 非常相似:
MyHTMLHelpers.cs:
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc.Rendering;
using System;
namespace MyApp.Helpers
{
public static class MyHTMLHelpers
{
public static IHtmlContent HelloWorldHTMLString(this IHtmlHelper htmlHelper)
=> new HtmlString("<strong>Hello World</strong>");
public static String HelloWorldString(this IHtmlHelper htmlHelper)
=> "<strong>Hello World</strong>";
}
}
_ViewImports.cshtml(第二行是重要的变化):
@using MyApp
@using MyApp.Helpers
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
MyView.cshtml:
<div>@Html.HelloWorldHTMLString()</div>
<div>@Html.HelloWorldString()</div>
输出:
你好世界
你好世界
【讨论】:
我从来没有让 HtmlHelper 扩展方法工作,我总是收到:
“IHtmlHelper”不包含“MethodName”的定义,并且找不到接受“IHtmlHelper”类型的第一个参数的扩展方法“MethodName”(是您缺少 using 指令或程序集引用?)
即使我的 _ViewImports.cshtml 文件中有正确的命名空间。所以我决定使用 Razor 页面的能力来支持注入已经注册为依赖注入的服务。例如,我需要将配置文件中的一些值注入到我的 _Layout.cshtml 文件中。所以我做了以下事情:
1) 定义了一个IConfigurationHelperService接口:
public interface IConfigurationHelperService
{
string GetApiUrl();
}
2) 在 ConfigurationHelperSerivce 类中定义了该接口的实现(该类本身使用依赖注入来获取常规配置类):
public class ConfigurationHelperService : IConfigurationHelperService
{
public ConfigurationHelperService(IConfiguration configuration)
{
Configuration = configuration;
}
private IConfiguration Configuration { get; }
public string GetApiUrl()
{
return GetConfigurationValue(ApiUrl);
}
private string GetConfigurationValue(string key)
{
var value = Configuration[key];
if (value.IsNullOrEmpty()) throw new KeyNotFoundException($"Configruation does not contain an instance of {key}");
return value;
}
}
3) 在 Startup.cs 中通过 ConfigureServices 注册注入服务:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IConfigurationHelperService, ConfigurationHelperService>();
services.AddMvc();
}
4) 将正确的命名空间作为 using 语句添加到我的 _ViewImports.cshtml 文件中。
5) 使用 @inject 关键字定义它以在 _Layout.cshtml 文件中使用。
@inject IConfigurationHelperService ConfigHelper
<!DOCTYPE html>
<html>
...
@ConfigHelper.GetApiUrl()
...
</html>
它对我来说非常有用,而且我可以在更简单的页面上看到它的更多用途,因为定义模型的工作量太大。
【讨论】:
对我而言,我认为我的 HTML 助手无法正常工作,直到我发现扩展方法现在位于 IHtmlHelper 而不是 HtmlHelper。
所以对于 .net 核心:
public static IHtmlContent CheckboxListFor<TModel>(this IHtmlHelper<TModel> html,
Expression<Func<TModel, List<CheckboxListItem>>> expression) ...
而不是 .net:
public static HtmlString CheckboxListFor<TModel>(this HtmlHelper<TModel> html,
Expression<Func<TModel, List<CheckboxListItem>>> expression) ...
编辑:我还将 .net 核心的返回类型更新为 IHtmlContent,因为使用 HtmlContentBuilder 之类的东西是编写 HTML 内容和返回返回IHtmlContent
【讨论】:
好吧,我想这个答案不会被注意到,但这是我使用服务注册想出的:
我希望它对某人有所帮助。
注册服务:
services.AddTransient<IHtmlHelperFactory, HtmlHelperFactory>();
使用服务:
var helper = HttpContext.RequestServices.GetRequiredService<IHtmlHelperFactory>().Create();
界面:
public interface IHtmlHelperFactory
{
IHtmlHelper Create();
}
实施:
public class HtmlHelperFactory : IHtmlHelperFactory
{
private readonly IHttpContextAccessor _contextAccessor;
public class FakeView : IView
{
/// <inheritdoc />
public Task RenderAsync(ViewContext context)
{
return Task.CompletedTask;
}
/// <inheritdoc />
public string Path { get; } = "View";
}
public HtmlHelperFactory(IHttpContextAccessor contextAccessor)
{
_contextAccessor = contextAccessor;
}
/// <inheritdoc />
public IHtmlHelper Create()
{
var modelMetadataProvider = _contextAccessor.HttpContext.RequestServices.GetRequiredService<IModelMetadataProvider>();
var tempDataProvider = _contextAccessor.HttpContext.RequestServices.GetRequiredService<ITempDataProvider>();
var htmlHelper = _contextAccessor.HttpContext.RequestServices.GetRequiredService<IHtmlHelper>();
var viewContext = new ViewContext(
new ActionContext(_contextAccessor.HttpContext, _contextAccessor.HttpContext.GetRouteData(), new ControllerActionDescriptor()),
new FakeView(),
new ViewDataDictionary(modelMetadataProvider, new ModelStateDictionary()),
new TempDataDictionary(_contextAccessor.HttpContext, tempDataProvider),
TextWriter.Null,
new HtmlHelperOptions()
);
((IViewContextAware)htmlHelper).Contextualize(viewContext);
return htmlHelper;
}
}
【讨论】:
这是一个使用 TagBuilders 的 .Net Core 2 示例
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc.Rendering;
using System.IO;
public static IHtmlContent HelloWorld(this IHtmlHelper html, string name)
{
var span = new TagBuilder("span");
span.InnerHtml.Append("Hello, " + name + "!");
var br = new TagBuilder("br") {TagRenderMode = TagRenderMode.SelfClosing};
string result;
using (var writer = new StringWriter())
{
span.WriteTo(writer, System.Text.Encodings.Web.HtmlEncoder.Default);
br.WriteTo(writer, System.Text.Encodings.Web.HtmlEncoder.Default);
result = writer.ToString();
}
return new HtmlString(result);
}
【讨论】:
WriteTo 部分。谢谢(y)
这是一个根据视图中的 Enum 值获取 Enum 名称的示例。枚举类型的自定义 HTML 帮助器
public static IHtmlContent DisplayEnumFor(this IHtmlHelper htmlHelper, string value, Type enumType)
{
if (htmlHelper == null)
throw new ArgumentNullException(nameof(htmlHelper));
if (value == null)
throw new ArgumentNullException(nameof(value));
if (!enumType.IsEnum)
throw new ArgumentException("Type must be an enumerated type");
foreach (var item in Enum.GetValues(enumType))
if (((int)item).ToString().Equals(value.Trim()))
return new HtmlString(item.ToString());
return new HtmlString(value);
}
【讨论】:
要创建自定义 HTML 助手,您需要创建一个静态类和静态方法。
以下示例是用于提交按钮的自定义 HTML 帮助器。
namespace TagHelpers.Helpers
{
public static class CustomHtmlHelpers
{
public static IHtmlContent SubmitButton(this IHtmlHelper htmlHelper, string value, string name )
{
string str = "<input type='submit' value ='"+ value +"'name='"+ name +"' />";
return new HtmlString(str);
}
}
}
确保在下面添加 using 语句。
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc.Rendering;
要在页面上的任何位置访问帮助程序,您需要将命名空间添加到 viewimports.cshtml 文件中
@using TagHelpers.Helpers
现在,您现在可以在要定义按钮的页面上使用它了。
<div>
@Html.SubmitButton("Login", "Command")
@Html.SubmitButton("Cancel", "Command")
</div>
【讨论】: