Search code examples
c#asp.net-mvcrazorasp.net-mvc-5html-helper

How do I avoid repetitive inline conditionals for defining htmlAttributes of Html.EditorFor()


I am building a form and I have to keep using an inline conditional to add a readonly html attribute:

@Html.LabelFor(model => model.EventDate)
<div class="row">
    <div class="col-xs-3">
        @Html.EditorFor(model => model.EventDate, new
        {
            htmlAttributes = Model.IsEditorReadOnly ?
                (object)new { @class = "form-control input-lg", @type = "date", @readonly = "readonly" } :
                (object)new { @class = "form-control input-lg", @type = "date" }
        })
    </div>
</div>
@Html.ValidationMessageFor(model => model.EventDate)

You can't use a conditional for just the @readonly property's value because even if it is set to null, it will be rendered out to the client as readonly="" and that is enough for a browser to make that field read-only.

There has to be a better way to do this than an inline conditional for every form element just to add a single attribute, right?


Solution

  • Thanks to Steven Muecke for all the assistance (give him all your up-votes above in the comments and at his linked answers). Here's the solution.

    For a model with this property:

    [Display(Name = "Event Date")]
    [DataType(DataType.Date)]
    [DisplayFormat(DataFormatString = "{0:MM-dd-yyyy}", ApplyFormatInEditMode = true)]
    [Range(typeof(DateTime), "01-01-2010", "12-31-2030")]
    public DateTime? EventDate { get; set; }
    

    Create this extension method:

    public static IHtmlString ReadOnlyEditorFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, object
    htmlAttributes = null, bool isReadOnly = false)
    {
        IDictionary<string, object> attributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
        if (isReadOnly)
        {
            attributes.Add("readonly", "readonly");
        }
    
        return html.EditorFor(expression, new { htmlAttributes = attributes });
    }
    

    And then use it in the view like this:

    @Html.ReadOnlyEditorFor(model => model.EventDate, 
        new { @class = "form-control input-lg", @type = "date" }, 
        Model.IsEditorReadOnly)
    

    And all of the model's property's meta data will appear for the first instance that it is called on the page. The resulting html will look like this:

    <input class="form-control input-lg text-box single-line" data-val="true" data-val-date="The field Event Date must be a date." data-val-range="The field Event Date must be between 1/1/2010 12:00:00 AM and 12/31/2030 12:00:00 AM." data-val-range-max="12/31/2030 00:00:00" data-val-range-min="01/01/2010 00:00:00" id="EventDate" name="EventDate" type="date" value="08-01-2015" />