MVC3 EditorTemplate для обнуляемого булеана с помощью RadioButtons

18

У меня есть свойство на одном из моих объектов, которое является нулевым логическим, я хочу, чтобы моя логика имела значение true, да, false - No и null для N / A. Теперь, поскольку я собираюсь иметь несколько свойств, подобных этому на многих разных объектах, имеет смысл сделать редактор и отобразить шаблоны для этих свойств. Я собираюсь использовать jQuery UI, чтобы применить визуальный элемент набора кнопок после того, как все это работает, но на данный момент это выходит за рамки моей проблемы. Мой шаблон редактора выглядит следующим образом.

@model bool?
<div data-ui="buttonset">
@Html.RadioButtonFor(x => x, true, new { id = ViewData.TemplateInfo.GetFullHtmlFieldId("") + "Yes"}) <label for="@(ViewData.TemplateInfo.GetFullHtmlFieldId(""))Yes">Yes</label>
@Html.RadioButtonFor(x => x, false, new { id = ViewData.TemplateInfo.GetFullHtmlFieldId("") + "No" }) <label for="@(ViewData.TemplateInfo.GetFullHtmlFieldId(""))No">No</label>
@Html.RadioButtonFor(x => x, "null", new { id = ViewData.TemplateInfo.GetFullHtmlFieldId("") + "NA" }) <label for="@(ViewData.TemplateInfo.GetFullHtmlFieldId(""))NA">N/A</label>
</div>

Моя проблема заключается в том, что ни при каких обстоятельствах я не могу заставить этот шаблон редактора правильно отображать текущее значение модели. Поскольку я не предоставляю свойство модели в этой области, но сама модель, встроенная логика в MVC3 не будет корректно устанавливать проверенное свойство из-за проверки, сделанной для проверки того, что имя не пустое или пустое (см. MVC3 source, InputExtensions.cs: строка # 259). Я не могу установить проверенный атрибут динамически по сравнению с моделью, потому что браузер проверяет радиообъект на наличие проверенного атрибута, а не на его значение, поэтому, хотя мои переключатели будут выглядеть следующим образом, последний из них остается выбранным .

<div class="span-4" data-ui="buttonset">
<input checked="checked" id="MyObject_BooleanValueYes" name="MyObject.BooleanValue" type="radio" value="True" /><label for="MyObject_BooleanValueYes">Yes</label>
<input checked="" id="MyObject_BooleanValueNo" name="MyObject.BooleanValue" type="radio" value="False" /><label for="MyObject_BooleanValueNo">No</label>
<input checked="" id="MyObject_BooleanValueNA" name="MyObject.BooleanValue" type="radio" value="null" /><label for="MyObject_BooleanValueNA">N/A</label>
</div><span class="field-validation-valid" data-valmsg-for="MyObject.BooleanValue" data-valmsg-replace="true"></span>&nbsp;</div>

Я не могу условно добавить HtmlAttribute, используя что-то вроде if?truevalue:falsevalue , потому что истинные / ложные части будут иметь разные анонимные типы, и я получаю сообщение об ошибке.

Я борюсь за то, как это должно быть сделано, и я надеюсь, что у одного из вас есть предложение о том, как решить эту проблему?

    
задан Nick Albrecht 19.07.2011 в 20:13
источник
  • Яррет Ярретт, я думаю, более элегантно –  Pal 02.04.2015 в 15:26

5 ответов

27
@model bool?
<div data-ui="buttonset">
@{
    Dictionary<string, object> yesAttrs = new Dictionary<string, object>(); 
    Dictionary<string, object> noAttrs = new Dictionary<string, object>(); 
    Dictionary<string, object> nullAttrs = new Dictionary<string, object>(); 

    yesAttrs.Add("id", ViewData.TemplateInfo.GetFullHtmlFieldId("") + "Yes");
    noAttrs.Add("id", ViewData.TemplateInfo.GetFullHtmlFieldId("") + "No");
    nullAttrs.Add("id", ViewData.TemplateInfo.GetFullHtmlFieldId("") + "NA");

    if (Model.HasValue && Model.Value)
    {
        yesAttrs.Add("checked", "checked");
    }
    else if (Model.HasValue && !Model.Value)
    {
        noAttrs.Add("checked", "checked");
    }
    else
    {
        nullAttrs.Add("checked", "checked");
    }
}

@Html.RadioButtonFor(x => x, "true", yesAttrs) <label for="@(ViewData.TemplateInfo.GetFullHtmlFieldId(""))Yes">Yes</label>
@Html.RadioButtonFor(x => x, "false", noAttrs) <label for="@(ViewData.TemplateInfo.GetFullHtmlFieldId(""))No">No</label>
@Html.RadioButtonFor(x => x, "null", nullAttrs) <label for="@(ViewData.TemplateInfo.GetFullHtmlFieldId(""))NA">N/A</label>
</div>
    
ответ дан Adam Flanagan 22.07.2011 в 10:51
источник
  • Это должно сработать. Я думаю, что, возможно, я слишком догнал всю эту «магическую» MVC + бритву. Это очень чисто и легко читать, но иногда я думаю, что я слишком привык к этому, и мне нужно отступить и признать, что не все может вписаться в одну строку. Я попробую, как только смогу. –  Nick Albrecht 22.07.2011 в 21:00
  • Да, это работает, но я бы не назвал это элегантным. В верхней части он скрыт в шаблоне редактора, поэтому вам не нужно его видеть! –  Adam Flanagan 22.07.2011 в 21:08
  • @AdamFlanagan, он работает, но каким-то образом он отображает лаборатории Yes / No / Null и рядом с ними соответствующие радиолюбители ... Любая идея, как скрыть их? –  Romias 15.10.2011 в 21:30
  • @Romias Вы просто хотите, чтобы переключатели без ярлыков? –  Adam Flanagan 16.10.2011 в 11:04
  • Отличное решение! Но мне это удалось заставить, изменив RadioButton на «null» на пустую строку (AKA «»). В противном случае я продолжал получать ModelState.IsValid = false, говоря, что поле не может быть «null». –  programatique 24.10.2013 в 20:18
Показать остальные комментарии
2

Как насчет некоторого метода расширения, чтобы сохранить эту «одну строку, чтобы управлять ими всеми». : -)

public static class DictionaryHelper
{
    // This returns the dictionary so that you can "fluently" add values
    public static IDictionary<TKey, TValue> AddIf<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, bool addIt, TKey key, TValue value)
    {
        if (addIt)
            dictionary.Add(key, value);
        return dictionary;
    }
}

И затем в вашем файле шаблона вы просто меняете подпись того, как вы добавляете дополнительные элементы, включая атрибут checked="checked".

@model bool?
<div data-ui="buttonset">
@Html.RadioButtonFor(x => x, true, new Dictionary<string,object>()
    .AddIf(true, "id", ViewData.TemplateInfo.GetFullHtmlFieldId("") + "Yes")
    .AddIf(Model.HasValue && Model.Value, "checked", "checked")
) <label for="@(ViewData.TemplateInfo.GetFullHtmlFieldId(""))Yes">Yes</label>

@Html.RadioButtonFor(x => x, false, new Dictionary<string,object>()
    .AddIf(true, "id", ViewData.TemplateInfo.GetFullHtmlFieldId("") + "No")
    .AddIf(Model.HasValue && !Model.Value, "checked", "checked")
) <label for="@(ViewData.TemplateInfo.GetFullHtmlFieldId(""))No">No</label>

@Html.RadioButtonFor(x => x, "null", new Dictionary<string,object>()
    .AddIf(true, "id", ViewData.TemplateInfo.GetFullHtmlFieldId("") + "NA")
    .AddIf(!Model.HasValue, "checked", "checked")
) <label for="@(ViewData.TemplateInfo.GetFullHtmlFieldId(""))NA">N/A</label>
</div>
    
ответ дан Norman H 26.12.2012 в 20:07
источник
  • , где идет первый блок кода? –  user3281466 22.04.2015 в 16:36
  • @ user3281466: Где угодно, если пространство имен, в котором он живет, включено там, где вы его используете. Это метод расширения. :) –  Gone Coding 16.09.2015 в 16:04
  • Мне очень нравится это как общее решение условных атрибутов (так +1), но новое решение @Canvas намного проще и отлично работает. –  Gone Coding 16.09.2015 в 16:07
  • Возможно, это было что-то, что я создал для MVC2 с учетом сроков ответа. –  Norman H 16.09.2015 в 21:12
1

Вам просто нужно обработать специальный нулевой случай:

<label class="radio">
  @Html.RadioButtonFor(x => x.DefaultBillable, "", new { @checked = !this.Model.DefaultBillable.HasValue })
  Not set
</label>
<label class="radio">
  @Html.RadioButtonFor(x => x.DefaultBillable, "false")
  Non-Billable
</label>
<label class="radio">
  @Html.RadioButtonFor(x => x.DefaultBillable, "true")
  Billable
</label>
    
ответ дан JarrettV 16.04.2013 в 23:11
источник
  • Проблема с этим решением заключается в том, что если атрибут «checked» существует вообще во входе, браузер предполагает, что он должен быть проверен. Даже если атрибут имеет значение false (checked="False"), браузер все равно проверяет радио, поскольку атрибут checked существует вообще. Вокруг этого есть хаки, но факт остается фактом: вы не можете использовать условную логику при установке проверяемого атрибута. –  John Washam 06.06.2013 в 16:40
  • это больше не работает –  user3281466 22.04.2015 в 16:35
1

Проблема заключается в том, что вам нужно установить атрибут checked , потому что Html.RadioButtonFor не проверяет переключатель на основе nullable bool (что кажется недостатком).

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

Общий / EditorTemplates / Boolean.cshtml

@model bool?
<label>
    <span>n/a</span>
    @Html.RadioButtonFor(x => x, "", !Model.HasValue ? new { @checked=true } : null) 
</label>
<label>
    <span>Yes</span>
    @Html.RadioButtonFor(x => x, true, Model.GetValueOrDefault() ? new { @checked = true } : null)
</label>
<label>
    <span>No</span>
    @Html.RadioButtonFor(x => x, false, Model.HasValue && !Model.Value ? new { @checked = true } : null)
</label>
    
ответ дан Canvas 16.09.2015 в 15:54
источник
  • Я искал всюду для простого решения этого. Отлично сработано. Работает! +1 –  Gone Coding 16.09.2015 в 16:03
0

Вы можете использовать @helper для упрощения принятого ответа:

@model bool?

<div data-ui="buttonset">
    @Radio(true,  "Yes", "Yes")
    @Radio(false, "No",  "No")
    @Radio(null,  "N/A", "NA")
</div>

@helper Radio(bool? buttonValue, string buttonLabel, string buttonId)
{
    Dictionary<string, object> attrs = new Dictionary<string, object>(); 

    // Unique button id
    string id = ViewData.TemplateInfo.GetFullHtmlFieldId("") + buttonId;

    attrs.Add("id", id);

    // Check the active button
    if (Model == buttonValue)
    {
        attrs.Add("checked", "checked");
    }

    @Html.RadioButtonFor(m => m, buttonValue, attrs) <label for="@id">@buttonLabel</label>
}
    
ответ дан buffjape 08.06.2017 в 17:35
источник
  • Это правда, однако стоит отметить, что это доступно только в MVC5 и ранее. MVC Core не имеет вспомогательных методов. –  Nick Albrecht 08.06.2017 в 18:47
  • Это огромный шаг назад, его очень грустно. –  buffjape 09.06.2017 в 09:50
  • Да и нет. В настоящее время существуют альтернативы, такие как ViewComponents, в качестве альтернативы ChildActions, если у вас включен C # 7, вы можете использовать Локальные функции, если вам нужен только многоразовый блок кода. Или, если вы хотите отображать HTML (например, ваше предложение), есть TagHelpers. Просто больше обучения - все :-) –  Nick Albrecht 09.06.2017 в 21:17
  • Не просто обучение. Большие веб-сайты широко использовали помощников, и теперь их нужно будет переписать программистам, которые стоят денег на работу - гораздо больше, чем лицензия VS. Microsoft должна была быть более ответственной, поддерживая поддержку помощников, но предупреждая, что они должны быть устаревшими. –  buffjape 12.06.2017 в 13:47