2

I have a list of checkboxes that i want to validate on client side with jQuery but failing. I have already added unobtrusive and jquery validation plugin to my project.

The Model code is:

 [Required]
 public string name { get; set; }

 [SkillValidation(ErrorMessage = "Select at least 3 skills")]
 public List<CheckBox> skills { get; set; }

and other model is:

public class CheckBox
{
    //Value of checkbox 
    public int Value { get; set; }
    //description of checkbox 
    public string Text { get; set; }
    //whether the checkbox is selected or not
    public bool IsChecked { get; set; }
}

Explanation - SkillValidation() is the custom attribute which i have created to do server side validation.

The SkillValidation class code is:

public class SkillValidation : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        List<CheckBox> instance = value as List<CheckBox>;
        int count = instance == null ? 0 : (from p in instance
                                            where p.IsChecked == true
                                            select p).Count();
        if (count >= 3)
            return ValidationResult.Success;
        else
            return new ValidationResult(ErrorMessage);
    }
}

Explanation: This code will validate user to check at least 3 checkboxes on the server side. I did not inherited this class from IClientValidatable interface because i know it wont be possible to do validation from the MVC way (unobtrusive manner).

My View code is:

@model demo.MVC.Models.CB
@{
    HtmlHelper.ClientValidationEnabled = true;
    HtmlHelper.UnobtrusiveJavaScriptEnabled = true;
}

@using (Html.BeginForm())
{
    <table>
        <tr>
            <td>
                @Html.LabelFor(model => model.name)
                @Html.EditorFor(model => model.name)
                @Html.ValidationMessageFor(model => model.name)
            </td>
            <td>
                @Html.LabelFor(model => model.skills)
                @Html.CheckBoxFor(m => m.skills[0].IsChecked, new { id = "csharpSkill" }) C#
                @Html.CheckBoxFor(m => m.skills[1].IsChecked, new { id = "aspSkill" }) ASP.NET
                @Html.CheckBoxFor(m => m.skills[2].IsChecked, new { id = "jquerySkill" }) jQuery
                @Html.CheckBoxFor(m => m.skills[3].IsChecked, new { id = "mvcSkill" }) ASP.NET MVC
                @Html.CheckBoxFor(m => m.skills[4].IsChecked, new { id = "razorSkill" }) Razor
                @Html.CheckBoxFor(m => m.skills[5].IsChecked, new { id = "htmlSkill" }) HTML
                @Html.ValidationMessageFor(model => model.skills)
            </td>
        </tr>
        <tr><td colspan="2"><button id="submitButton" type="submit">Submit</button></td></tr>
    </table>
}

@Scripts.Render("~/jQuery")
@Scripts.Render("~/jQueryValidate")
@Scripts.Render("~/Unobtrusive")

Explanation: In the view i have created the text box for name and 6 checkboxes for the skills created with @Html.CheckBoxFor().

Problem: The problem is that If i remove the 6 checkboxes then client side validation works well for the name text box.

If i put 6 checkboxes and press the button then only the server side validation works for name and checkboxe.

I want client side validation to work for the 6 checkboxes too so that user has to select 3 checkboxes at least.

How can i achieve it ?

thanks

1 Answer 1

6

You cannot achieve that using MVC's client side validation (by implementing IClientValidatable and using jquery.validation.unobtrusive.js). The reason is that client side validation rules are applied to form controls, and you do not (and cannot) create a form control for your skills property which is a collection, not a simple value type.

You need to write your own scripts to validate the number of checked checkboxes (and if not valid, make use of the placeholder generated by @Html.ValidationMessageFor(model => model.skills)

To mimic jquery's 'lazy' validation, initially handle the .submit() event, and there after, handle the .click() event of the checkboxes.

Modify your 2nd <td> element to add an id attribute for selecting the checkboxes (see also notes below)

<td id="skills">
    .... // your checkboxes

and add the following scripts

var validateSkillsOnCheck = false; // for lazy validation
var requiredSkills = 3;
var skills = $('#skills input[type="checkbox"]');
var errorMessage = 'Select at least 3 skills';
var errorElement = $('span[data-valmsg-for="skills"]');

// function to validate the required number of skills
function validateSkills() {
    var selectedSkills = skills.filter(':checked').length;
    var isValid = selectedSkills > requiredSkills;
    if (!isValid) {
        errorElement.addClass('field-validation-error').removeClass('field-validation-valid').text(errorMessage);
    } else {
        errorElement.addClass('field-validation-valid').removeClass('field-validation-error').text('');
    }
    return (isValid);
}

$('form').submit(function () {
    validateSkillsOnCheck = true;
    if (!validateSkills()) {
        return false; // prevent submit
    }
});
$('#skills').on('click', 'input', function () {
    if (validateSkillsOnCheck) {
        validateSkills();
    }
})

A few side notes.

  1. Tables are for tabular data, not layout and using a <table> element is not appropriate in your case.
  2. Your @Html.LabelFor(model => model.skills) is not appropriate (you do not have a form control for skills so clicking on it does not set focus to anything). That should just be a <span>@Html.DisplayNameFor(m =>m.skills)</span> or similar element.
  3. You should however be creating labels for each checkbox. Your model has 3 properties including Text and Value and its not clear what the difference is between them, and in any case, you never include them in the view. I assume your will want to submit at least the Value property so you know which skills have been selected

    <label>
        @Html.CheckBoxFor(m =>m.skills[i].IsChecked)
        <span>@Model.skills[i].Text</span>
    </label>
    @Html.HiddenFor(m =>m.skills[i].Value)
    
Sign up to request clarification or add additional context in comments.

11 Comments

Although your jQuery code worked but i have a little problem. Suppose when a user comes to the form and selects - 4 skills but does not puts his name. In that case the client side jQuery should give him the error for name field. This is not happening because the page is getting postback on button click. Name property is already [Required]. Why it is happening?
I mean to say the when a user selects the required skills and press the submit button the the application does run the client side validations of other properties at all.
Your form will not submit and you will get a client side error if the user does not fill in the name textbox (and I have already tested that). Are you sure the jquery.validate.js and jquery.validate.unobtrusive.js files are loaded?
And not related, but why do you have HtmlHelper.ClientValidationEnabled = true; and HtmlHelper.UnobtrusiveJavaScriptEnabled = true; in your view. They are true by default (and should be in your web.config.cs file.
yes jquery validate.js and jquery validate unobrusive.js are loaded. I checked it by removing the whole 6 checkboxes (second td contents) from the view, the validation for name are working on client side. why it is behaving so, is it microsoft bug?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.