Search code examples
asp.net-mvcasp.net-validators

ASP.Net MVC: User must select one check box validate at client side


I have UI where i am showing 3 checkboxes and each refer to different property of model class. i am using jquery unobtrusive validation just by mvc data annotation. i want when user submit form then user has to select one checkbox otherwise client side error message will display and form will not be submitted.

i can do it by jquery but i want to do it by mvc data annotation.

see my model class

public class Customer
{
    [Required]
    [Display(Name = "First Name")]
    public string FirstName { get; set; }

    [Required]
    [Display(Name = "Last Name")]
    public string LastName { get; set; }

    [Display(Name = "Mail to me")]
    public bool SelfSend { get; set; }

    [Display(Name = "3rd party")]
    public bool thirdParty { get; set; }

    [Display(Name = "Others")]
    public bool Others { get; set; }
}

Controller

[ValidateAntiForgeryToken()]
[HttpPost]
public ActionResult Index(Customer customer)
{
    if (customer.Others == false || customer.SelfSend == false || customer.thirdParty == false)
        ModelState.AddModelError("Error", "Must select one option");

    return View();
}

with the below code i can validate any checkboxes is selected or not from server side code and add model error which show error at client side.

but i want to do validation by client side using normal data annotation.

see my razor code

<div class="row">
    <div class="col-md-8">
        <section id="testform">
            @using (Html.BeginForm("Index", "Customers", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
            {
                @Html.AntiForgeryToken()
                <h4>Enter customer info.</h4>
                <hr />
                @Html.ValidationSummary(true, "", new { @class = "text-danger" })
                <div class="form-group">
                    @Html.LabelFor(m => m.FirstName, new { @class = "col-md-2 control-label" })
                    <div class="col-md-10">
                        @Html.TextBoxFor(m => m.FirstName, new { @class = "form-control" })
                        @Html.ValidationMessageFor(m => m.FirstName, "", new { @class = "text-danger" })
                    </div>
                </div>
                <div class="form-group">
                    @Html.LabelFor(m => m.LastName, new { @class = "col-md-2 control-label" })
                    <div class="col-md-10">
                        @Html.TextBoxFor(m => m.LastName, new { @class = "form-control" })
                        @Html.ValidationMessageFor(m => m.LastName, "", new { @class = "text-danger" })
                    </div>
                </div>
                <div class="form-group">
                    <div class="col-md-offset-2 col-md-10">
                        <div class="checkbox">
                            @Html.CheckBoxFor(m => m.SelfSend)
                            @Html.LabelFor(m => m.SelfSend)
                        </div>
                    </div>

                    <div class="col-md-offset-2 col-md-10">
                        <div class="checkbox">
                            @Html.CheckBoxFor(m => m.thirdParty)
                            @Html.LabelFor(m => m.thirdParty)
                        </div>
                    </div>

                    <div class="col-md-offset-2 col-md-10">
                        <div class="checkbox">
                            @Html.CheckBoxFor(m => m.Others)
                            @Html.LabelFor(m => m.Others)
                        </div>
                    </div>
                    <div class="col-md-offset-2 col-md-10">
                        @Html.ValidationMessage("Error", "", new { @class = "text-danger" })
                    </div>
                    </div>
                <div class="form-group">
                    <div class="col-md-offset-2 col-md-10">
                        <input type="submit" value="Save" class="btn btn-default" />
                    </div>
                </div>

            }
        </section>
    </div>
</div>

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

Solution

  • You can try to write a customer model validation attribute.

    add CheckBoxAuthAttribute in your one of three validation property.

    There is a method protected virtual ValidationResult IsValid(object value, ValidationContext validationContext) in you can override inValidationAttribute.

    public class CheckBoxAuthAttribute : ValidationAttribute
    {
        public CheckBoxAuthAttribute(params string[] propertyNames)
        {
            this.PropertyNames = propertyNames;
        }
    
        public string[] PropertyNames { get; private set; }
    
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            var properties = this.PropertyNames.Select(validationContext.ObjectType.GetProperty);
            var values = properties
                    .Select(p => p.GetValue(validationContext.ObjectInstance, null))
                    .OfType<bool>();
    
            if (values.Contains(true) || (bool)value == true)
            {
                return null;
            }
            return new ValidationResult(this.FormatErrorMessage(validationContext.DisplayName));
        }
    }
    public class Customer
    {
        [Required]
        [Display(Name = "First Name")]
        public string FirstName { get; set; }
    
        [Required]
        [Display(Name = "Last Name")]
        public string LastName { get; set; }
    
        [Display(Name = "Mail to me")]
        [CheckBoxAuth("thirdParty", "Others", ErrorMessage = "Must select one option"))]
        public bool SelfSend { get; set; }
    
        [Display(Name = "3rd party")]
        public bool thirdParty { get; set; }
    
        [Display(Name = "Others")]
        public bool Others { get; set; }
    }