如何让 asp-for 输入标签助手生成驼峰命名法名称?

Posted

技术标签:

【中文标题】如何让 asp-for 输入标签助手生成驼峰命名法名称?【英文标题】:How to make the asp-for input tag helper generate camelCase names? 【发布时间】:2017-04-11 22:10:57 【问题描述】:

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

 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 输入标签助手生成类似上面的骆驼案例名称​​ 不必让我的模型属性为骆驼案例?

【问题讨论】:

你真的在乎吗?如果 taghelper 生成 name="StartDate" 但您希望控制器方法使用名称 startDate,则模型绑定器会为您处理。 我很在乎。因为我可以通过其 id 使用客户端 javascript 访问该字段,并且我希望该代码遵循我的代码库的编码约定。 【参考方案1】:

在研究了@Bebben 发布的代码和提供的链接之后,我继续深入挖掘 Asp.Net Core 源代码。而且我发现 Asp.Net Core 的设计者提供了一些扩展点,可以利用这些点来实现更低的驼峰式 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); 
    


【讨论】:

您的回答让我想到了我需要的东西,即用点替换为 ids 生成的该死的下划线。应该注意的是,您使用的 services.Remove&lt;TService, TImplementation&gt;() 不是内置扩展,必须单独创建,或者至少据我所知似乎是这样。 感谢您的评论。好点子。我将使用该服务的代码更新我的答案。删除扩展方法。 用飞机犁地...?这不是“有点”太多的开销吗……? @MladenB.应该几乎没有开销,只有在需要时将字符串操作小写为单个前导字符的开销。【参考方案2】:

最简单的方法就是写

<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 License, Version 2.0, Copyright .NET Foundation)

该行是“For.Name”。该名称被发送到其他一些方法中,最终给出最终名称的是静态类(Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.NameAndIdProvider),所以我们不能轻易插入。

【讨论】:

您的回答启发了我深入研究源代码,我发现可以实现系统范围的方法。我第一次真正开始体会到使用 IoC 容器构建系统的强大功能。 @RonC 这是个好消息 :-)

以上是关于如何让 asp-for 输入标签助手生成驼峰命名法名称?的主要内容,如果未能解决你的问题,请参考以下文章

Oracle为啥不支持驼峰命名法

大驼峰命名法和小驼峰命名法的区别

更新 Spring Data JPA 之数据表名的命名规则为驼峰命名法

java标识符命名规范之驼峰命名法

怎么用java实现驼峰命名法与数据库命名法的相互转换

驼峰命名帕斯卡命名匈牙利命名--三种命名方法