Checkbox has to be 'checked' - with unobtrusive jQuery validation and ASP.NET MVC 3

Is it a pretty common scenario to have a checkbox on a form, that is required to be checked during POST-ing of a form. This usually happens when we have a typical ‘Agree to Terms and Conditions’ element. When I started to play with unobtrusive JavaScript validation in new ASP.NET MVC i was pretty sure that decorating boolean property with [Required] attribute will solve that issue. Surprisingly it doesn’t. The key to this problem lies in interpretation of jQuery validation ‘required’ rule. I digged a little and find a specific code inside a jquery.validate.unobtrusive.js file:

adapters.add("required", function (options) {
  // jQuery Validate equates "required" with "mandatory" for checkbox elements
  if (options.element.tagName.toUpperCase() !== "INPUT" || options.element.type.toUpperCase() !== "CHECKBOX") {
    setValidationValues(options, "required", true);
  }
});

As You can see rule does not applies to checkboxes. Simple solution to that would be to either change this code (which we really do not want to do) or create a new specific adapter. I do not know why it is like that - i can guess that it might be for a backward compatibility. Anyway, to make that work we will start by creating a custom attribute.

public class BooleanRequired : RequiredAttribute, IClientValidatable {        

        public BooleanRequired() {
            
        }

        public override bool IsValid(object value) {
            return value != null && (bool)value == true;
        }

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

As You can see it inherits from common RequiredAttribute. Moreover it implements IClientValidateable. This is to make assure that rule will be propagated to client side (jQuery validation) as well. The key part here is GetClientValidationRules method (adapter) where we define new, custom ValidationType called for the purpose of this post, as a ‘brequired’. To make that work, a specific JavaScript code has to be prepare that adds our custom adapter:

jQuery.validator.unobtrusive.adapters.add("brequired", function (options) {
    //b-required for checkboxes
    if (options.element.tagName.toUpperCase() == "INPUT" && options.element.type.toUpperCase() == "CHECKBOX") {
        //setValidationValues(options, "required", true);
        options.rules["required"] = true;
        if (options.message) {
            options.messages["required"] = options.message;
        }
    }
});

All that is left is to mark properties with newly created BooleanRequired attribute.

[BooleanRequired]
public bool AcceptTermsAndCond { get; set' }

Happy coding!

comments powered by Disqus