问题
Is it possible in ASP.Net Core to automatically convert camel case property names in view models to insert spaces into the corresponding labels when using tag helpers?
If my view model looks like this...
[Display(Name = "First Name")]
public string FirstName { get; set; }
[Display(Name = "Last Name")]
public string LastName { get; set; }
[Display(Name = "Referral Date")]
public DateTime ReferralDate { get; set; }
It seems like a lot of extra configuration applying data annotations such as
[Display(Name = "First Name")]
to simply insert a space between words. It would make sense that Tag Helpers would insert the space by default to avoid this manual configuration and potential typos.
If not could a custom tag helper assist in this situation and if so how would it work?
回答1:
You can achieve this by building and registering a custom display metadata provider. There are libraries that will perform more elaborate "humanization" to the property names, but you can achieve something pretty useful with some trusty regular expressions.
public class HumanizerMetadataProvider : IDisplayMetadataProvider
{
public void CreateDisplayMetadata(DisplayMetadataProviderContext context)
{
var propertyAttributes = context.Attributes;
var modelMetadata = context.DisplayMetadata;
var propertyName = context.Key.Name;
if (IsTransformRequired(propertyName, modelMetadata, propertyAttributes))
{
modelMetadata.DisplayName = () => SplitCamelCase(propertyName);
}
}
private static string SplitCamelCase(string str)
{
return Regex.Replace(
Regex.Replace(
str,
@"(\P{Ll})(\P{Ll}\p{Ll})",
"$1 $2"
),
@"(\p{Ll})(\P{Ll})",
"$1 $2"
);
}
private static bool IsTransformRequired(string propertyName, DisplayMetadata modelMetadata, IReadOnlyList<object> propertyAttributes)
{
if (!string.IsNullOrEmpty(modelMetadata.SimpleDisplayProperty))
return false;
if (propertyAttributes.OfType<DisplayNameAttribute>().Any())
return false;
if (propertyAttributes.OfType<DisplayAttribute>().Any())
return false;
if (string.IsNullOrEmpty(propertyName))
return false;
return true;
}
}
The IsTransformRequired
method ensures that you can still override the provider with a decorated property when you need to.
Register the provider on startup through the AddMvcOptions
method on IMvcBuilder
:
builder.AddMvcOptions(m => m.ModelMetadataDetailsProviders.Add(new HumanizerMetadataProvider()));
回答2:
If you only care about label, you can easily override LabelTagHelper.
[HtmlTargetElement("label", Attributes = "title-case-for")]
public class TitleCaseTagHelper : LabelTagHelper
{
public TitleCaseTagHelper(IHtmlGenerator generator) : base(generator)
{
}
[HtmlAttributeName("title-case-for")]
public new ModelExpression For { get; set; }
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
if (context == null)
throw new ArgumentNullException("context");
if (output == null)
throw new ArgumentNullException("output");
string name = For.ModelExplorer.Metadata.DisplayName ?? For.ModelExplorer.Metadata.PropertyName;
name = name.Humanize(LetterCasing.Title);
TagBuilder tagBuilder = this.Generator.GenerateLabel(
this.ViewContext,
this.For.ModelExplorer,
this.For.Name,
name,
(object) null);
if (tagBuilder == null)
return;
output.MergeAttributes(tagBuilder);
if (output.IsContentModified)
return;
TagHelperContent childContentAsync = await output.GetChildContentAsync();
if (childContentAsync.IsEmptyOrWhiteSpace)
output.Content.SetHtmlContent((IHtmlContent) tagBuilder.InnerHtml);
else
output.Content.SetHtmlContent((IHtmlContent) childContentAsync);
}
}
Usage
<label title-case-for="RememberMe" class="col-md-2 control-label"></label>
Please ensure to place using statement and @addTagHelper inside _ViewImports.cshtml.
@using YourNameSpace.Helpers
@addTagHelper *, YourNameSpace
Note
I use Humanizer English Only NuGet Package - Humanizer.Core. It is more robust than writing my own method. If you doesn't like overhead, you can just use Regular Expression.
回答3:
how about using a custom attribute and overriding DisplayNameAttribute
public class DisplayWithSpace : DisplayNameAttribute
{
public DisplayWithSpace([System.Runtime.CompilerServices.CallerMemberName] string memberName ="")
{
Regex r = new Regex(@"(?!^)(?=[A-Z])");
DisplayNameValue = r.Replace(memberName, " ");
}
}
and your property will be
[DisplayWithSpace]
public string FatherName { get; set; }
来源:https://stackoverflow.com/questions/42905563/asp-net-core-custom-tag-helper-to-convert-camelcase-properties-to-spaces