Используйте Html.RadioButtonFor и Html.LabelFor для той же модели, но разные значения

У меня есть этот шаблон Razor

<table>
<tr>
    <td>@Html.RadioButtonFor(i => i.Value, "1")</td>
    <td>@Html.LabelFor(i => i.Value, "true")</td>
</tr>
<tr>
    <td>@Html.RadioButtonFor(i => i.Value, "0")</td>
    <td>@Html.LabelFor(i => i.Value, "false")</td>
</tr>
</table>

Это дает мне этот HTML

<table>
<tr>
    <td><input id="Items_1__Value" name="Items[1].Value" type="radio" value="1" /></td>
    <td><label for="Items_1__Value">true</label></td>
</tr>
<tr>
    <td><input checked="checked" id="Items_1__Value" name="Items[1].Value" type="radio" value="0" /></td>
    <td><label for="Items_1__Value">false</label></td>
</tr>
</table>

Итак, у меня есть ID Items_1__Value дважды, что, конечно же, не очень хорошо и не работает в браузере, когда я нажимаю на вторую метку "false", первая радиостанция будет активирована.

Я знаю, что могу добавить собственный идентификатор в RadioButtonFor и обратиться к этому с помощью моего ярлыка, но это не очень хорошо, не так ли? Особенно потому, что я в цикле и не могу просто использовать имя "значение" с добавленным номером, которое также будет в конечном итоге в нескольких идентификаторах в моей окончательной разметке HTML.

Не должно быть хорошего решения для этого?

Ответ 1

Мне было интересно, как MVC определяет "вложенные" имена полей и идентификаторы. Потребовалось немного исследований исходного кода MVC, чтобы понять, но я думаю, что у меня есть хорошее решение для вас.

Как EditorTemplates и DisplayTemplates определяют имена полей и идентификаторы

С введением EditorTemplates и DisplayTemplates в среду MVC добавлена ​​ ViewData.TemplateInfo, которая содержит, помимо прочего, текущий "префикс поля", такой как "Items[1].". Вложенные шаблоны используют это для создания уникальных имен и идентификаторов.

Создайте наши собственные уникальные идентификаторы:

Класс TemplateInfo содержит интересный метод, GetFullHtmlFieldId. Мы можем использовать это для создания наших собственных уникальных идентификаторов, например:

@{string id = ViewData.TemplateInfo.GetFullHtmlFieldId("fieldName");}
@* This will result in something like "Items_1__fieldName" *@

Для Win

Здесь, как добиться правильного поведения для вашего примера:

<table>
<tr>
    @{string id = ViewData.TemplateInfo.GetFullHtmlFieldId("radioTrue");}
    <td>@Html.RadioButtonFor(i => i.Value, "1", new{id})</td>
    <td>@Html.LabelFor(i => i.Value, "true", new{@for=id})</td>
</tr>
<tr>
    @{id = ViewData.TemplateInfo.GetFullHtmlFieldId("radioFalse");}
    <td>@Html.RadioButtonFor(i => i.Value, "0", new{id})</td>
    <td>@Html.LabelFor(i => i.Value, "false", new{@for=id})</td>
</tr>
</table>

Что даст вам следующий HTML:

<table>
<tr>
    <td><input id="Items_1__radioTrue" name="Items[1].Value" type="radio" value="1" /></td>
    <td><label for="Items_1__radioTrue">true</label></td>
</tr>
<tr>
    <td><input checked="checked" id="Items_1__radioFalse" name="Items[1].Value" type="radio" value="0" /></td>
    <td><label for="Items_1__radioFalse">false</label></td>
</tr>
</table>

Отказ

Синтаксис My Razor недостаточно развит, поэтому, пожалуйста, дайте мне знать, есть ли у этого кода синтаксические ошибки.

Для чего стоит

Очень печально, что эта функциональность не встроена в RadioButtonFor. Кажется логичным, что все отображаемые кнопки радио должны иметь идентификатор, который является комбинацией его name AND value, но это не так - возможно, потому, что это будет отличаться от всех других хелм-хелперов.
Создание собственных методов расширения для этой функциональности также кажется логичным выбором. Тем не менее, это может оказаться сложным с использованием синтаксиса выражения... поэтому я рекомендую перегружать .RadioButton(name, value, ...) вместо RadioButtonFor(expression, ...). И вам может понадобиться перегрузка для .Label(name, value).
Я надеюсь, что все имело смысл, потому что в этом пункте много "заполнить пробелы".

Ответ 2

Не переустраивайте решение для этого. Все, что вы пытаетесь сделать, это заставить переключатели реагировать на щелчки на тексте. Держите его простым и просто оберните свои переключатели ярлыками меток:

<table>
<tr>
    <td><label>@Html.RadioButtonFor(i => i.Value, "1")True</label></td>
</tr>
<tr>
    <td><label>@Html.RadioButtonFor(i => i.Value, "0")False</label></td>
</tr>
</table>

Помощник LabelFor html обычно используется для ввода имени из атрибута Display в вашей модели просмотра (например, "[Display (Name =" Enter your Name ")]).

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

Ответ 3

@Scott Rippey почти имеет его, но я думаю, он должен использовать для меня другую версию MVC3, потому что для меня @Html.LabelFor нет перегрузок, которые будут принимать 3 аргумента. Я обнаружил, что использование обычного @Html.Label работает просто отлично:

@{string id = ViewData.TemplateInfo.GetFullHtmlFieldId("radioButton_True");}
@Html.Label(id, "True:")
@Html.RadioButtonFor(m => m.radioButton, true, new { id })

@{id = ViewData.TemplateInfo.GetFullHtmlFieldId("radioButton_False");}
@Html.Label(id, "False:")
@Html.RadioButtonFor(m => m.radioButton, false, new { id })

это позволяет вам щелкнуть по метке и выбрать соответствующую радиокамеру, как вы ожидали.

Ответ 4

Не идеально, но работайте,

<table>
<tr>
    <td>@ReplaceName(Html.RadioButtonFor(i => i.Value, "1"))</td>
    <td>@Html.LabelFor(i => i.Value[0], "true")</td>
</tr>
<tr>
    <td>@ReplaceName(Html.RadioButtonFor(i => i.Value, "0"))</td>
    <td>@Html.LabelFor(i => i.Value[1], "false")</td>
</tr>
</table>


@functions {
    int counter = 0;
    MvcHtmlString ReplaceName(MvcHtmlString html){
        return MvcHtmlString.Create(html.ToString().Replace("__Value", "__Value_" + counter++ +"_"));
    }
}

Ответ 5

Здесь можно использовать HtmlHelper, хотя вы можете настроить его. Лишь едва протестировал, поэтому YMMV.

     public static MvcHtmlString RadioButtonWithLabelFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression, object value, object htmlAttributes = null)
     {
        var name = ExpressionHelper.GetExpressionText(expression);
        var id = helper.ViewData.TemplateInfo.GetFullHtmlFieldId(name + "_" + value);

        var viewData = new ViewDataDictionary(helper.ViewData) {{"id", id}};

        if (htmlAttributes != null)
        {
            var viewDataDictionary = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
            foreach (var keyValuePair in viewDataDictionary)
            {
                viewData[keyValuePair.Key] = keyValuePair.Value;
            }
        }

        var radioButton = helper.RadioButtonFor(expression, value, viewData);

        var tagBuilder = new TagBuilder("label");
        tagBuilder.MergeAttribute("for", id);
        tagBuilder.InnerHtml = value.ToString();

        return new MvcHtmlString(radioButton.ToHtmlString() + tagBuilder.ToString());
    }

Ответ 6

Вы можете добавить текст с тегами

<td>@Html.RadioButtonFor(i => i.Value, true) <text>True</text></td>
<td>@Html.RadioButtonFor(i => i.Value, false) <text>False</text></td>

Ответ 7

По-видимому, существует хорошее решение

    <td>@Html.RadioButtonFor(i => i.Value, true)</td>
    <td>@Html.RadioButtonFor(i => i.Value, false)</td>