【问题标题】:MVC3 EditorTemplate for a nullable boolean using RadioButtons使用 RadioButtons 的可空布尔值的 MVC3 EditorTemplate
【发布时间】:2011-07-19 18:13:59
【问题描述】:

我的一个对象上有一个可以为空的布尔值的属性,我希望我的逻辑具有 true 表示 Yes,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 中的内置逻辑不会正确设置选中的属性,因为进行了检查以验证名称是否为空或 null(参见 MVC3源,InputExtensions.cs:line#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>

我不能使用 if?truevalue:falsevalue 之类的东西有条件地添加 HtmlAttribute,因为真/假部分将是不同的匿名类型,我得到一个错误。

我正在努力解决这个问题,希望你们中的某个人对如何解决这个问题提出建议?

【问题讨论】:

  • JarrettV 的回答我觉得更优雅

标签: asp.net-mvc asp.net-mvc-3 mvc-editor-templates


【解决方案1】:
@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>

【讨论】:

  • 应该可以。我想我可能太沉迷于整个单行“魔术” MVC + razor 让你完成。它非常干净且易于阅读,但有时我认为我已经习惯了它,需要退后一步并承认并非所有内容都可以放在一行中。我会尽快尝试。
  • 是的,它有效,但我不会称之为优雅。从好的方面来说,它隐藏在编辑器模板中,因此您无需查看!
  • @AdamFlanagan,它可以工作,但它以某种方式显示标签 Yes/No/Null 并在它们旁边显示相应的单选按钮......知道如何隐藏它们吗?
  • @Romias 你只想要没有标签的单选按钮吗?
  • 很好的解决方案!但我只能通过将带有“null”的 RadioButton 更改为空字符串(AKA“”)来实现这一点。否则我一直收到 ModelState.IsValid = false 说该字段不能为“null”。
【解决方案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>

【讨论】:

  • @user3281466:你喜欢的任何地方,只要它所在的命名空间包含在你使用它的地方。这是一个扩展方法。 :)
  • 我真的很喜欢这个作为条件属性的通用解决方案(所以+1),但是@Canvas 的新解决方案要简单得多,而且效果很好。
  • 考虑到答案的时间范围,这可能是我为 MVC2 精心打造的。
【解决方案3】:

问题是您需要设置checked 属性,因为Html.RadioButtonFor 不会检查基于可为空的布尔值的单选按钮(这似乎是一个缺陷)。

另外,通过将单选按钮放在标签标签内,您可以通过单击标签来选择值。

Shared/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>

【讨论】:

  • 我到处寻找一个简单的解决方案。做得好。工作一种享受! +1
【解决方案4】:

您只需要像这样处理特殊的 null 情况:

<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>

【讨论】:

  • 这个解决方案的问题是,如果输入中存在“checked”属性,浏览器会认为它应该被检查。即使该属性的值为 false (checked="False"),浏览器仍会检查收音机,因为已检查的属性完全存在。对此有一些技巧,但事实仍然是在设置 checked 属性时不能使用条件逻辑。
【解决方案5】:

您可以使用@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>
}

【讨论】:

  • 这是真的,但值得注意的是,这仅在 MVC5 及更早版本中可用。 MVC Core 没有辅助方法。
  • 这是一个巨大的倒退,非常可悲。
  • 是和不是。现在有替代品,例如 ViewComponents 作为 ChildActions 的替代品,如果您启用了 C#7,如果您只需要可重用的代码块,则可以使用 Local Functions。或者,如果您想呈现 HTML(如您的建议),则有 TagHelpers。只是更多的学习:-)
  • 不只是学习。大型网站已经广泛使用助手,而这些现在必须由花钱雇用的程序员重新编写 - 远远超过 VS 许可费。 Microsoft 应该更加负责,保持对帮助程序的支持,但警告他们将被弃用。
【解决方案6】:

使用上面的Canvas 示例我构建了一个自定义模型和视图,因此您可以通过模型控制值并在代码中编辑它们,布尔值并不总是是/否/(n/a),所以这是它的方法在 MVC5 中查找。

对可为空的布尔值使用通用模型

public class Customisable_NullableRadioBool
    {
        public bool? Result { get; set; }
        public string TrueLabel { get; set; }
        public string FalseLabel { get; set; }
        public string NullLabel { get; set; }
        public string AttributeTitle { get; set; }
    }

下面是要存储的 CSHTML: ~/Views/Shared/EditorTemplates/Customisable_NullableRadioBool.cshtml

@model Customisable_NullableRadioBool
@Model.AttributeTitle<br />
<div class="control-group">
    <label>
        @Html.RadioButtonFor(x => x.Result, "", !Model.Result.HasValue ? new { @checked = true } : null)
        <span>@Model.NullLabel</span>
    </label>
    <br />
    <label>
        @Html.RadioButtonFor(x => x.Result, true, Model.Result.GetValueOrDefault() ? new { @checked = true } : null)
        <span>@Model.TrueLabel</span>
    </label>
    <br />
    <label>
        @Html.RadioButtonFor(x => x.Result, false, Model.Result.HasValue && !Model.Result.Value ? new { @checked = true } : null)
        <span>@Model.FalseLabel</span>
    </label>
</div>

然后您可以通过项目的其余部分引用泛型类和编辑器模板,并像这样呈现编辑器模板。

@Html.EditorFor(m => m.YourCustomisable_NullableBool, "Customisable_NullableRadioBool")

以及渲染的输出示例

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-11-26
    • 1970-01-01
    • 2011-10-14
    • 2017-03-26
    • 2015-11-18
    • 2013-05-09
    • 1970-01-01
    相关资源
    最近更新 更多