Make EditorFor render decimal value as type=“text”

2019-08-11 00:39发布

问题:

I have two properties in a model class:

public int? IntTest { get; set; }
public decimal? DecimalTest { get; set; }

Which I then render with:

@Html.EditorFor(model => model.IntTest, new { htmlAttributes = new { @class = "form-control"} })
@Html.EditorFor(model => model.DecimalTest, new { htmlAttributes = new { @class = "form-control"} })

I'd expected both of them to render as html inputs of type number, but the decimal one doesn't, I get:

<input class="form-control text-box single-line" data-val="true" data-val-number="The field IntTest must be a number." id="IntTest" name="IntTest" type="number" value="" />
<input class="form-control text-box single-line" data-val="true" data-val-number="The field IntTest must be a number." id="DecimalTest" name="DecimalTest" type="text" value="" />

The decimal value is rendered as type="text" whereas the int is registered as type="number".

This question implies that isn't the expected behaviour so am I doing something wrong?

If that is the expected behaviour, is there any way of changing EditorFor to render all decimals as type="number", rather than having to add type = "number" in the htmlAttributes of every decimal field?

回答1:

The html you are seeing is the default behavior. The EditorFor() methods use the default templates (unless you have created a custom EditorTemplate for the type) as defined in TemplateHelpers.cs.

For typeof int (and byte and long), it uses the NumberInputTemplate, and for typeof decimal it uses the DecimalTemplate. These templates are defined in DefaultEditorTemplates.cs which are for decimal

internal static string DecimalTemplate(HtmlHelper html)
{
    if (html.ViewContext.ViewData.TemplateInfo.FormattedModelValue == html.ViewContext.ViewData.ModelMetadata.Model)
    {
        html.ViewContext.ViewData.TemplateInfo.FormattedModelValue = String.Format(CultureInfo.CurrentCulture, "{0:0.00}", html.ViewContext.ViewData.ModelMetadata.Model);
    }
    return StringTemplate(html);
}

which in turn calls

internal static string StringTemplate(HtmlHelper html)
{
    return HtmlInputTemplateHelper(html);
}

and for int

internal static string NumberInputTemplate(HtmlHelper html)
{
    return HtmlInputTemplateHelper(html, inputType: "number");
}

Note that the NumberInputTemplate defines the inputType as "number" which adds the type="number" attribute, where as StringTemplate uses the default inputType which generates type="text".

To add type="number" for a decimal, then you need to manually add the attribute, using either

@Html.EditorFor(m => m.DecimalTest, new { htmlAttributes = new { type = "number", @class = "form-control"} })

or

@Html.TextBoxFor(m => m.DecimalTest, new { type = "number", @class = "form-control"})

An alternative would be to create a custom EditorTemplate in the /Views/Shared/EditorTemplates/Decimal.cshtml for typeof decimal, for example

@model decimal?
@{
    var attributes = HtmlHelper.AnonymousObjectToHtmlAttributes(ViewData["htmlAttributes"]);
    if (!attributes.ContainsKey("type"))
    {
         attributes.Add("type", "number");
    }
    string formatString = ViewData.ModelMetadata.DisplayFormatString ?? "{0:N2}";
}
@Html.TextBoxFor(m => m, formatString , attributes)

and in the main view use

@Html.EditorFor(model => model.DecimalTest, new { htmlAttributes = new { @class = "form-control"} })

Another alternative would be to create you own HtmlHelper extension method (say @Html.DecimalFor(...)) to generate the html.