How to validate a list of checkbox through custom

2019-04-17 14:14发布

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条回答
萌系小妹纸
2楼-- · 2019-04-17 15:03

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)
    
查看更多
登录 后发表回答