Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

unobtrusive MVC3 validating group of checkboxes

I need to validate a group of checkboxes using MVC3 unobtrusive validation. how would i do it? I found this and tried it, but it doesn't work.

$(function(){

        $.validator.addMethod('cb_selectone', function(value,element){
            if(element.length>0){
                for(var i=0;i<element.length;i++){
                    if($(element[i]).val('checked')) return true;
                }
                return false;
            }
            return false;
        }, 'Please select at least one option');

        $('#main').validate({rules:{Services:"cb_selectone"}});

...

My Html:

<input type="checkbox" class="checkbox" name="Services" value="1" />
<input type="checkbox" class="checkbox" name="Services" value="2" />
<input type="checkbox" class="checkbox" name="Services" value="3" />

It would be best if someone provided a complete solution with server side + client side validation (of course using MVC3 unobtrusive validation).

Thanks

like image 842
ShaneKm Avatar asked Apr 14 '11 11:04

ShaneKm


2 Answers

Ok, figured it out:

for server validation: using data annotations (required will do it)

like so in my view model:

[Required(ErrorMessageResourceName = "fld_Service_val_Required_lbl", ErrorMessageResourceType = typeof(Resources.Service.Controllers.Firm))]
public ICollection<int> Services { get; set; }

for client validation in my html i added a class to my input checkboxes:

<input type="checkbox" class="checkbox required-checkbox" name="Services" value="1" />
<input type="checkbox" class="checkbox required-checkbox" name="Services" value="2" />
<input type="checkbox" class="checkbox required-checkbox" name="Services" value="3" />

and also:

$(function(){
        $.validator.addMethod('required_group', function(value, element) {
            var $module = $(element).parents('form');
            return $module.find('input.checkbox:checked').length;
        }, 'Select at least one Service please');
        $.validator.addClassRules('required-checkbox', { 'required_group' : true });  

..

not sure if this is the best solution but it works :). if someone knows a better please post.

like image 121
ShaneKm Avatar answered Nov 12 '22 00:11

ShaneKm


This works - validates appropriately on submit, and hides/displays the validation message if a checkbox is subsequently checked or unchecked with minimal overhead (only gets hit once per validation cycle).

JavaScript:

(function ($) {
    //functions from unobtrusive:
    function setValidationValues(options, ruleName, value) {
        options.rules[ruleName] = value;
        if (options.message) {
            options.messages[ruleName] = options.message;
        }
    }
    var formEls;
    function getUniqueFormId(form) {
        if (typeof(formEls==='undefined')) {
            formEls = document.getElementsByTagName('form');
        }
        return 'form' + Array.prototype.indexOf.call(formEls, form);
    }
    //from jQuery validation
    function findByName(name, form) {
        // select by name and filter by form for performance over form.find("[name=...]")
        return $(document.getElementsByName(name)).map(function (index, element) {
            return element.form == form && element.name == name && element || null;
        });
    }
    //-------------------------
    $.validator.addMethod('requiredgroup', function (value, element, params) {
        for (var i = 0; i < params.length; i++) {
            if (params[i].checked) { return true; }
        }
        return false;
    });
    valGroups = [];
    $.validator.unobtrusive.adapters.add('requiredgroup', function (options) {
        var groupName = options.element.name,
            uniqueGroup = getUniqueFormId(options.form) + groupName;
        if (!valGroups[uniqueGroup]) {
            valGroups[uniqueGroup] = findByName(groupName, options.form);
        }
        //jQuery Validation Plugin 1.9.0 only ever validates first chekcbox in a list
        //could probably work around setting this for every element:
        setValidationValues(options, 'requiredgroup', valGroups[uniqueGroup]);
    });
} (jQuery));

of course the elements must have a data-val-requiredgroup attribute. The following MVC code (as part of a helper) will add the appropriate annotations:

 var metaData = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
 string name = ExpressionHelper.GetExpressionText(expression);

 var baseAttr = htmlHelper.GetUnobtrusiveValidationAttributes(name, metaData);

  baseAttr.Add("name", name);

  baseAttr.Add("type", "checkbox");
  if (metaData.IsRequired) 
   {
       baseAttr.Add("data-val-requiredgroup", baseAttr["data-val-required"]);
       baseAttr.Remove("data-val-required");

Because it looks for the Required attribute, server side validation is handled by the existing framework.

like image 4
Brent Avatar answered Nov 12 '22 00:11

Brent