Question

I am unhappy with the current DropDownList implementation, because I can't really do much with the option tags (only selected, text and value is supported). I want to make my own where I can set disabled and other stuff on individual options.

Currently I'm altering the options by javascript, but I think it's a bit of a hacky way to do it, and I'd prefer to just render the correct html to begin with.

I know I can just make a template that uses select and option tags and make the options as I want them - but the normal DropDownList extension adds stuff val stuff and a specific name and ID which I guess is for proper databinding when submitting the form:

<select data-val="true" data-val-number="The field SelectedValue must be a number." id="ParentDropDown_SelectedValue" name="ParentDropDown.SelectedValue">

How do I go about adding these attributes to my own templates?

Was it helpful?

Solution

You are right, those attributes (and specially the name attribute) are critical for the model binding.

Say you want to create a custom helper like

public static MvcHtmlString CustomHelperFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression)

First you can use var fieldName = ExpressionHelper.GetExpressionText(expression); to get the field name.

Then use var fullBindingName = html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(fieldName); in order to get the full name, taking care of nested views.

Finally you can transform this into an id attribute using var fieldId = TagBuilder.CreateSanitizedId(fullBindingName);.

So a simple custom helper that creates a textbox could be written as:

public static MvcHtmlString CustomHelperFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression)
{            
    var fieldName = ExpressionHelper.GetExpressionText(expression);
    var fullBindingName = html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(fieldName);
    var fieldId = TagBuilder.CreateSanitizedId(fullBindingName);

    var metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
    var value = metadata.Model;

    TagBuilder tag = new TagBuilder("input");
    tag.Attributes.Add("name", fullBindingName);
    tag.Attributes.Add("id", fieldId);
    tag.Attributes.Add("type", "text");
    tag.Attributes.Add("value", value == null ? "" : value.ToString());

    var validationAttributes = html.GetUnobtrusiveValidationAttributes(fullBindingName, metadata);
    foreach (var key in validationAttributes.Keys)
    {
        tag.Attributes.Add(key, validationAttributes[key].ToString());
    }

    return new MvcHtmlString(tag.ToString(TagRenderMode.SelfClosing));
}

You can use it in a view like:

@Html.CustomHelperFor(model => model.ParentDropDown.SelectedValue)

And it will produce the following html:

<input id="ParentDropDown_SelectedValue" name="ParentDropDown.SelectedValue" type="text" value="4">

Hope it helps!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top