在ASP.Net Core中创建自定义HTML助手

65

我想要创建自己的HTML Helper,就像在ASP.NET MVC中使用的那种,但是我还没有找到正确实现的方法。

我已经发现如何创建自定义的标签助手(Tag Helpers),但是不知道如何创建自己的HTML Helper。我应该如何创建自己的HTML Helper呢?

9个回答

88

对我来说,我曾认为我的HTML辅助程序没起作用,直到我发现扩展方法现在位于IHtmlHelper而不是HtmlHelper

因此,对于.net core

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 core 的返回类型为 IHtmlContent,使用像 HtmlContentBuilder 这样的东西来组成 HTML 内容并返回 IHtmlContent 更加方便。


3
没注意到我的错误中的区别,应该尝试使用界面。谢谢! - James Eby

62

HTML助手似乎得到了ASP.NET Core的支持,但还没有文档:

https://learn.microsoft.com/en-au/aspnet/core/mvc/views/html-helpers

[编辑:]自回答以来,上述页面已不再存在。 我会说HTML Helpers虽然可用,但在ASP.NET Core中不再得到“支持”。

从ASP.NET Core源代码来看,它们与旧版ASP.NET MVC非常相似:

https://github.com/aspnet/AspNetCore/blob/master/src/Mvc/Mvc.ViewFeatures/src/Rendering/HtmlHelperDisplayExtensions.cs


示例

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(第二行是重要更改):

_T_ViewImports.cshtml(第二行有重要变化):
@using MyApp
@using MyApp.Helpers
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

MyView.cshtml:

<div>@Html.HelloWorldHTMLString()</div>
<div>@Html.HelloWorldString()</div>

输出:

Hello World

<strong>Hello World</strong>


1
源代码链接已过期(404)。当前链接为:https://github.com/aspnet/AspNetCore/blob/master/src/Mvc/Mvc.ViewFeatures/src/Rendering/HtmlHelperDisplayExtensions.cs - tie
这应该在微软的帮助和文档页面上,我还没有尝试过,但我可以告诉这就是我要找的。微软更新你们的东西时应该加入像这样的例子。 - Seabizkit
哇,这个很简单,但我和谷歌花了一个小时才找到。需要在使用Razor视图模板发送电子邮件时,在DotNetCore 2.2中输出原始HTML。 - Matt

13

这里是一个使用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) - Jawand Singh

7

我想这个答案可能不会被注意,但是以下是使用服务注册时我想到的方法:

希望对某些人有所帮助。

注册服务:

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;
    }
}

6
我从未成功使用HtmlHelper扩展方法,总是遇到以下错误信息:
“'IHtmlHelper' does not contain a definition for 'MethodName' and no extension method 'MethodName' accepting a first argument of type 'IHtmlHelper' could be found (are you missing a using directive or an assembly reference?)”
即使我在_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>

它对我非常有效,在一些较为简单的页面上,如果定义模型会太麻烦,我可以看到它有更多用途。

1
我曾经遇到过同样的问题,通过将扩展类设为公共类来解决了这个问题。 - user626528

4

这个问题在Danny van der Kraan的博客文章中得到了很好的解释。下面的答案是从这篇文章中摘录出来的:

ASP.NET Core 1.0 [MVC 6]引入了一个新的令人兴奋的功能,叫做TagHelpers。在ASP.Net Core 1.0中,没有像MVC中的HTML Helper那样的概念。

什么是TagHelpers?

TagHelpers可以被看作是HTML Helpers的进化版,HTML Helpers是在第一个MVC框架推出时引入的。为了提供背景信息,你需要想象一下,在经典的ASP语言中,你唯一能自动生成HTML的方法就是通过自定义子程序。之后,ASP.NET带来了服务器控件,其中视图状态是最大的优点,以模拟桌面应用程序的外观和感觉,并帮助转型成为桌面开发人员。但我们都知道当我们试图把方形塞进圆形孔时会发生什么。我们不得不面对一个事实,那就是Web开发与桌面开发毫不相同。为了与正确的Web开发保持一致,ASP.NET MVC框架推出了HTML Helpers来自动输出HTML。但HTML Helpers从来没有真正地融合在一起,特别是对于前端开发人员和设计师而言。其中一个主要的抱怨是它会让你在视图开发过程中不断地从角括号(HTML,CSS)切换到C#(Razor syntax),这使得体验变得不必要的不舒适。 [MVC 6]想通过引入TagHelpers来解决这个问题以及一些更小的问题。例如,HTML Helper:

@Html.ActionLink(”Home”, ”Index”, ”Home”)

使用锚点TagHelper,代码应该如下所示:
<a asp-action="Index" asp-controller="Home">Home</a>

注意:asp-只是一种约定,稍后会详细说明。

在浏览器中呈现的输出结果是相同的:

<a href="/">Home</a>

PS:假设默认路由没有被更改。

有关TagHelpers的更多信息,请点击这里


我知道TagHelpers是一个新的和很棒的功能,但我需要使用类似于@Html.Raw的帮助程序在视图中插入HTML。我能用TagHelpers做到这一点吗? - Duefectu
TagHelperASP.Net Core的改进功能。使用TagHelper,您可以完成所有操作。但是,您能否举一些例子来解释一下您的意思? - MANISH KUMAR CHOUDHARY
1
感谢 @Manish,我理解了 TagHelpers 并且不需要使用 HtmlHelper。我找到了一份简单的好文档,它解决了我的问题:链接 - Duefectu
2
@MANISHKUMARCHOUDHARY 请阅读此帖子,了解正确归属来源的方法。在您的帖子底部放置链接是不够的。当复制他人内容时,应该在前面进行适当的归属。 - spender
@spender 感谢您指出我的错误,我已经更新了我的答案。 - MANISH KUMAR CHOUDHARY

3

要创建自定义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;

为了在页面的任何地方访问helper,您需要在viewimports.cshtml文件中添加命名空间。
@using TagHelpers.Helpers

现在,您可以在想要定义按钮的页面上使用它。

<div>
        @Html.SubmitButton("Login", "Command")
        @Html.SubmitButton("Cancel", "Command")
</div>

2

这里有一个示例,可以根据视图中的枚举值获取枚举名称。用于枚举类型的自定义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);
}

0
保持简单,但渲染效果符合预期。确保为正确的元素设置了正确的属性。如果需要改进,请提出建议,或者如果看起来不错,请给予支持。
 public static class IconExtensions
    {
        public static IHtmlContent CCIcon(string iconName, string? toolTip = null)
        {
            return new HtmlString($"<img src=\"/img/{iconName}.png\" alt=\"{iconName}\" class=\"img-ca-annexicons\" title=\"{toolTip??iconName}\" />");
        }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接