如何让asp-for输入标签帮助器生成camelCase名称?

时间:2017-04-11 22:10:57

标签: html asp.net-core-mvc tag-helpers

如果我有这样的视图模型:

 public class MyModel{
      public DateTime? StartDate {get;set;}
 }

在视图上,输入标记与asp-for标记助手一起使用,如下所示:

<input asp-for="StartDate" />

由此生成的默认html是

 <input type="datetime" id="StartDate" name="StartDate" value="" />

但我希望它生成的是html,如下所示:

 <input type="datetime" id="startDate" name="startDate" value="" />

如何让asp-for输入标签助手生成驼峰案例名称,如上面的,而必须使我的模型属性为camelCase?

2 个答案:

答案 0 :(得分:8)

在研究了@Bebben发布的代码及其提供的链接后,我继续深入研究Asp.Net Core源代码。我发现Asp.Net Core的设计者提供了一些可扩展点,可用于实现较低的camelCase idname值。

为此,我们需要实现自己的IHtmlGenerator,我们可以通过创建一个继承自DefaultHtmlGenerator的自定义类来实现。然后在该类上我们需要覆盖GenerateTextBox方法来修复外壳。或者我们可以覆盖GenerateInput方法来修复所有输入字段(不仅仅是输入文本字段)的nameid属性值的大小写,这是我选择做的。作为奖励,我还会覆盖GenerateLabel方法,因此标签的for属性也会使用自定义大小写指定值。

这是班级:

    using Microsoft.AspNetCore.Antiforgery;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Mvc.Internal;
    using Microsoft.AspNetCore.Mvc.ModelBinding;
    using Microsoft.AspNetCore.Mvc.Rendering;
    using Microsoft.AspNetCore.Mvc.Routing;
    using Microsoft.AspNetCore.Mvc.ViewFeatures;
    using Microsoft.Extensions.Options;
    using System.Collections.Generic;
    using System.Text.Encodings.Web;

    namespace App.Web {
        public class CustomHtmlGenerator : DefaultHtmlGenerator {

            public CustomHtmlGenerator(
                IAntiforgery antiforgery,
                IOptions<MvcViewOptions> optionsAccessor,
                IModelMetadataProvider metadataProvider,
                IUrlHelperFactory urlHelperFactory,
                HtmlEncoder htmlEncoder,
                ClientValidatorCache clientValidatorCache) : base
                                (antiforgery, optionsAccessor, metadataProvider, urlHelperFactory,
                                htmlEncoder, clientValidatorCache) {

               //Nothing to do

            }

            public CustomHtmlGenerator(
                IAntiforgery antiforgery,
                IOptions<MvcViewOptions> optionsAccessor,
                IModelMetadataProvider metadataProvider,
                IUrlHelperFactory urlHelperFactory,
                HtmlEncoder htmlEncoder,
                ClientValidatorCache clientValidatorCache,
                ValidationHtmlAttributeProvider validationAttributeProvider) : base
                                (antiforgery, optionsAccessor, metadataProvider, urlHelperFactory, htmlEncoder,
                                clientValidatorCache, validationAttributeProvider) {

                //Nothing to do

            }


            protected override TagBuilder GenerateInput(
                ViewContext viewContext,
                InputType inputType,
                ModelExplorer modelExplorer,
                string expression,
                object value,
                bool useViewData,
                bool isChecked,
                bool setId,
                bool isExplicitValue,
                string format,
                IDictionary<string, object> htmlAttributes) {

                expression = GetLowerCamelCase(expression);

                return base.GenerateInput(viewContext, inputType, modelExplorer, expression, value, useViewData, 
                                        isChecked, setId, isExplicitValue, format, htmlAttributes);
            }


            public override TagBuilder GenerateLabel(
                ViewContext viewContext,
                ModelExplorer modelExplorer,
                string expression,
                string labelText,
                object htmlAttributes) {

                expression = GetLowerCamelCase(expression);

                return base.GenerateLabel(viewContext, modelExplorer, expression, labelText, htmlAttributes);
            }


            private string GetLowerCamelCase(string text) {

                if (!string.IsNullOrEmpty(text)) {
                    if (char.IsUpper(text[0])) {
                        return char.ToLower(text[0]) + text.Substring(1);
                    }
                }

                return text;
            }

        }
    }

现在我们有了CustomHtmlGenerator类,我们需要在IoC容器中注册它来代替DefaultHtmlGenerator。我们可以通过以下两行在Startup.cs的ConfigureServices方法中执行此操作:

  //Replace DefaultHtmlGenerator with CustomHtmlGenerator
  services.Remove<IHtmlGenerator, DefaultHtmlGenerator>();
  services.AddTransient<IHtmlGenerator, CustomHtmlGenerator>();

非常酷。我们不仅解决了输入字段中的idname套管问题,而且通过实施我们自己的自定义IHtmlGenerator,并将其注册,我们已经打开了各种各样的可以完成的html自定义。

我开始非常欣赏围绕IoC构建的系统的强大功能,以及使用虚拟方法的默认类。在这种方法下,只需很少的努力即可获得定制水平,这真的非常棒。

<强>更新
@ Gup3rSuR4c指出我的services.Remove调用必须是一个未包含在框架中的扩展方法。我检查了,是的,这是真的。所以,这是扩展方法的代码:

 public static class IServiceCollectionExtensions {

    public static void Remove<TServiceType, TImplementationType>(this IServiceCollection services) {

        var serviceDescriptor = services.First(s => s.ServiceType == typeof(TServiceType) &&
                                                    s.ImplementationType == typeof(TImplementationType));
        services.Remove(serviceDescriptor); 
    }

}

答案 1 :(得分:4)

最简单的方法是编写

<input asp-for="StartDate" name="startDate" />

或者你想让它在驼峰的情况下完全自动生成整个应用程序吗?

为此,您似乎必须在Microsoft.AspNetCore.Mvc.TagHelpers中实现自己的InputTagHelpers。

以下是生成名称的方法:

private TagBuilder GenerateTextBox(ModelExplorer modelExplorer, string inputTypeHint, string inputType)
{
    var format = Format;
    if (string.IsNullOrEmpty(format))
    {
        format = GetFormat(modelExplorer, inputTypeHint, inputType);
    }

    var htmlAttributes = new Dictionary<string, object>
    {
        { "type", inputType }
    };

    if (string.Equals(inputType, "file") && string.Equals(inputTypeHint, TemplateRenderer.IEnumerableOfIFormFileName))
    {
        htmlAttributes["multiple"] = "multiple";
    }

    return Generator.GenerateTextBox(
        ViewContext,
        modelExplorer,
        For.Name,
        value: modelExplorer.Model,
        format: format,
        htmlAttributes: htmlAttributes);
}

(以上代码来自https://github.com/aspnet/Mvc/blob/dev/src/Microsoft.AspNetCore.Mvc.TagHelpers/InputTagHelper.cs,Apache许可证,版本2.0,版权所有.NET Foundation)

该行是&#34; For.Name&#34;。该名称被发送到其他一些方法,最后给出最终名称的方法是在静态类(Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.NameAndIdProvider)中,所以我们无法轻易插入任何内容。