可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have a property on one of my objects that is a nullable boolean, I want my logic to have true represent Yes, false to be No and null to be N/A. Now because I am going to have multiple properties like this on many different objects it makes the most sense to make an editor and display templates for these properties. I am going to use jQuery UI to apply a visual element of buttonset after this is all working but for now, that's beyond the scope of my problem. My editor template looks like this.
@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>
My problem is that under no circumstances can I get this editor template to show the current value of the model correctly. Because I am not rendering a property of a model at this scope but the model itself, the built in logic in MVC3 will not set the checked property correctly because of a check that is made to verify the name is not empty or null (See MVC3 source, InputExtensions.cs:line#259). I can't set the checked attribute dynamically by comparing to the Model because the browser checks the radiobutton on the presence of the checked attribute not it's value, so even though my radio buttons would look like the following the last one is still the one selected.
<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> </div>
I can't conditionally add the HtmlAttribute using something like if?truevalue:falsevalue
becuase the true/false parts would be of different anonymous types and I get an error.
I'm struggling on how this should be done and am hoping one of you have a suggestion on how to tackle this problem?
回答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>
回答2:
How about some extension method fun to keep that "one line to rule them all". :-)
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;
}
}
And then in your template file you simply change the signature of how you are adding the additional parameters including the checked="checked" attribute to the element.
@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>
回答3:
You just need to handle the special null case like so:
<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>
回答4:
The problem is that you need to set the checked
attribute because the Html.RadioButtonFor
does not check a radio button based on a nullable bool (which appears to be a flaw).
Also by putting the radio buttons inside of the label tag, you can select value by clicking the label.
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>
回答5:
You can use a @helper to simplify the accepted answer:
@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>
}