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 h
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.
@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>
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>
@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>
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>
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>
}
using Canvas example above I built a customization model and view so you can control the values via a model and edit them in code, bools aren't always a yes/no/(n/a) so Here's how it looks in MVC5.
using a generic model for the nullable bool
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; }
}
Here's the CSHTML to be stored in: ~/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>
And then you can reference the generic class and the editor template through the rest of your project and render the editor template like this.
@Html.EditorFor(m => m.YourCustomisable_NullableBool, "Customisable_NullableRadioBool")
And the rendered output examples