MVC unobtrusive validation on checkbox not working

Sniffer picture Sniffer · Aug 3, 2011 · Viewed 25.4k times · Source

I'm trying to implement the code as mentioned in this post. In other words I'm trying to implement unobtrusive validation on a terms and conditions checkbox. If the user hasn't selected the checkbox, then the input should be marked as invalid.

This is the server side Validator code, I've added:

/// <summary>
/// Validation attribute that demands that a boolean value must be true.
/// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public class MustBeTrueAttribute : ValidationAttribute
{
    public override bool IsValid(object value)
    {
        return value != null && value is bool && (bool)value;
    }
}

This is the model

[MustBeTrue(ErrorMessage = "You must accept the terms and conditions")]
[DisplayName("Accept terms and conditions")]
public bool AcceptsTerms { get; set; }

This is my view:

@Html.EditorFor(x => x.AcceptTermsAndConditions)
@Html.LabelFor(x => x.AcceptTermsAndConditions)
@Html.ValidationMessageFor(x => x.AcceptTermsAndConditions)

and this is the jQuery I've used to attach the validator client side:

$.validator.unobtrusive.adapters.addBool("mustbetrue", "required");

The client side script doesn't appear to be kicking in, however. Whenever I press the submit button, validation on the other fields kicks in fine, but the validation for the Terms & conditions doesn't seem to kick in. This is how the code looks in Firebug after I've clicked the submit button.

<input type="checkbox" value="true" name="AcceptTermsAndConditions" id="AcceptTermsAndConditions" data-val-required="The I confirm that I am authorised to join this website and I accept the terms and conditions field is required." data-val="true" class="check-box">
<input type="hidden" value="false" name="AcceptTermsAndConditions">
<label for="AcceptTermsAndConditions">I confirm that I am authorised to join this website and I accept the terms and conditions</label>
<span data-valmsg-replace="true" data-valmsg-for="AcceptTermsAndConditions" class="field-validation-valid"></span>

Any ideas? Have I missed out a step? This is driving me potty!

Thanks in advance S

Answer

Darin Dimitrov picture Darin Dimitrov · Aug 3, 2011

You need to implement IClientValidatable on your custom attribute in order to tie the mustbetrue adapter name that you are registering on the client side with this attribute:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public class MustBeTrueAttribute : ValidationAttribute, IClientValidatable
{
    public override bool IsValid(object value)
    {
        return value != null && value is bool && (bool)value;
    }

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        yield return new ModelClientValidationRule
        {
            ErrorMessage = this.ErrorMessage,
            ValidationType = "mustbetrue"
        };
    }
}

UPDATE:

Full working example.

Model:

public class MyViewModel
{
    [MustBeTrue(ErrorMessage = "You must accept the terms and conditions")]
    [DisplayName("Accept terms and conditions")]
    public bool AcceptsTerms { get; set; }
}

Controller:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        var model = new MyViewModel();
        return View(model);
    }

    [HttpPost]
    public ActionResult Index(MyViewModel model)
    {
        return View(model);
    }
}

View:

@model MyViewModel

<script src="@Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
<script type="text/javascript">
    $.validator.unobtrusive.adapters.addBool("mustbetrue", "required");
</script>

@using (Html.BeginForm())
{
    @Html.CheckBoxFor(x => x.AcceptsTerms)
    @Html.LabelFor(x => x.AcceptsTerms)
    @Html.ValidationMessageFor(x => x.AcceptsTerms)
    <input type="submit" value="OK" />
}