Создание собственного расширения HtmlHelper для ввода, которое работает с привязкой к модели

Я недоволен текущей реализацией DropDownList, потому что я не могу много сделать с тегами опций (поддерживается только выделение, текст и значение). Я хочу сделать свой собственный, где я могу установить отключенные и другие вещи по отдельным параметрам.

В настоящее время я изменяю параметры javascript, но я думаю, что это немного хакерский способ сделать это, и я предпочел бы просто отобразить правильный html для начала.

Я знаю, что могу просто создать шаблон, который использует теги select и option и сделать параметры, как я их хочу, но нормальное расширение DropDownList добавляет материал val и конкретное имя и идентификатор, которые, как я полагаю, предназначены для правильной привязки данных при отправке форма:

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

Как мне добавить эти атрибуты в свои собственные шаблоны?

Ответ 1

Вы правы, эти атрибуты (и особенно атрибут name) имеют решающее значение для привязки модели.

Предположим, вы хотите создать собственный помощник, например

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);, чтобы получить полное имя, заботясь о вложенных представлениях.

Наконец, вы можете преобразовать это в атрибут id с помощью var fieldId = TagBuilder.CreateSanitizedId(fullBindingName);.

Таким образом, простой пользовательский помощник, создающий текстовое поле, может быть записан как:

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));
}

Вы можете использовать его в виде:

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

И он произведет следующий html:

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

Надеюсь, что это поможет!