user2437066
user2437066

Reputation: 41

MVC Validation - Nullable Bool - Force true / false selection by radiobuttons

In an MVC application

Where a field is nullable bool type and is presented as a radiobutton set

How can client side validation be enforced by setting of a data attribute

Client side validation is being used (jquery unobtrusive)

As it stands, I have templated nullable bools to be presented to the user as radio buttons. There are 3 radio buttons, one for True, one for False and one for null.

The null radio button is hidden, so the user is to be forced to chose from the true or false option (null is not a valid option, null just means that the user has not made a selection yet - true or false must be chosen)

I have tried the following that I found as the answer to the same question elsewhere

[Range(typeof(bool), "false", "true", ErrorMessage = "You must make a selection")]

but this never passes validation

I have also simply tried

[Required]

but this does not force the user to make a selection

Further information:-

The problem appears to exist when the bool? is presented as radiobuttons. If using the MVC default template for bool? (which is a drop down list with 3 options, "Not Set", "True", "False" then validation works as expected.

When presented as radio butttons, the radio buttons are functional and save the correct state to the database, but validation does not work.

Example code

Model :-

public class SomeModel
{
    [Required]
    public string Name { get; set; }

    [Required]
    public bool? SomeBool { get; set; }
}

View :-

@using (Html.BeginForm())
{
    @Html.ValidationSummary(true)
    <fieldset>
        <div class="editor-label">
            @Html.LabelFor(model => model.Name)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Name)
            @Html.ValidationMessageFor(model => model.Name)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.SomeBool)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.SomeBool)
            @Html.ValidationMessageFor(model => model.SomeBool)
        </div>

        <p>
            <input type="submit" value="Create" />
        </p>

    </fieldset>
}

The above works as expected with no editor template set for bool, but not for radio buttons.

Radio button template :-

@model bool?
<div>
    @{
        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>
    <span style="display: none">
        @Html.RadioButtonFor(x => x, "null", nullAttrs) <label for="@(ViewData.TemplateInfo.GetFullHtmlFieldId(""))NA">N/A</label>
    </span>
</div>

The problem is going to be related to the client side validation data attributes not being created for the radio buttons

Upvotes: 1

Views: 3552

Answers (3)

Tony Phan
Tony Phan

Reputation: 11

Interestingly, a combination like below is all that's needed to get this to work. Without the [Required] attribute, not choosing a value is essentially seen as a false hence it passes validation. Adding the attribute makes it a required value.

[Required(ErrorMessage = "You must make a selection")]
[Range(typeof(bool), "false", "true", ErrorMessage = "You must make a selection")]

Upvotes: 0

shady youssery
shady youssery

Reputation: 440

Model:

public bool? boolObj { get; set; }
public int intObj { get; set; }

View:

@Html.TextBoxFor(p => p.boolObj )
@Html.TextBoxFor(p => p.intObj )

You can see from a view source what happens on the page:

<input data-val="true" data-val-required="The boolObj field is required." id="boolObj " name="boolObj " type="text" value="False">
<input data-val="true" data-val-number="The field intObj must be a number." data-val-required="The intObj field is required." id="intObj " name="intObj " type="text" value="0">

The nullable bool has no validation attributes, whereas the bool has a data-val-required tag. The int has a data-val-required tag and a data-val-number attribute

Of course, on a checkbox this is all pretty redundant as it can only be checked (true) or not checked (false) so a required tag isn't much use.

Upvotes: 0

Vladyslav Kushnir
Vladyslav Kushnir

Reputation: 391

You can write your small attribute for this purpose:

public class TrueFalseBoolAttribute: ValidationAttribute
{
    public override bool IsValid(Object value)
    {
        return value is bool;
    }
}

this should work for you. But I also think that if you want to have 3 options the best way would be Enum.

Upvotes: 2

Related Questions