Search code examples
asp.net-coreasp.net-core-mvcasp.net-core-3.1unobtrusive-validationclient-side-validation

Conditional client side validation in ASP.NET MVC Core 3.1


I want to make conditional validation in ASP.NET MVC Core 3.1. I have written custom validation and it works fine in server-side validation but I am unable to perform client-side validation. In my sample application, there is a Salary textbox, which is only required if the Role=Teacher is selected in the Roles dropdown. Could you please help me in the client-side validation part and here is the complete sample code.

Employee model class

public class Employee
    {
        public int Id { get; set; }

        [Required(ErrorMessage = "Please enter name")]
        public string Name { get; set; }

        [Required(ErrorMessage = "Please enter email")]
        [EmailAddress]
        public string Email { get; set; }

        [Required(ErrorMessage = "Please enter  role")]
        [EnumDataType(typeof(Roles))]
        public Roles? Role { get; set; }

        [Required(ErrorMessage = "Please Enter Hire Date")]
        [Display(Name = "Hire Date")]
        public DateTime? HireDate { get; set; }

        [RequiredIf("Role", Roles.Teacher, ErrorMessage = "Please enter salary")]
        public int? Salary { get; set; }
    }

Roles enum

 public enum Roles
    {
        Student = 1,
        Teacher = 2,
        Assistant = 3        
    }

RequiredIfAttribute custom validation class

public class RequiredIfAttribute : ValidationAttribute, IClientModelValidator
    {
        public string PropertyName { get; set; }
        public object Value { get; set; }

        public RequiredIfAttribute(string propertyName, object value, string errorMessage = "")
        {
            PropertyName = propertyName;
            ErrorMessage = errorMessage;
            Value = value;
        }

        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            var instance = validationContext.ObjectInstance;
            var type = instance.GetType();
            var proprtyvalue = type.GetProperty(PropertyName).GetValue(instance, null);
            if (proprtyvalue != null)
            {
                if (proprtyvalue.ToString() == Value.ToString() && value == null)
                {
                    return new ValidationResult(ErrorMessage);
                }
            }     
            return ValidationResult.Success;
        }

        public void AddValidation(ClientModelValidationContext context)
        {
            context.Attributes.Add("data-val", "true");
            context.Attributes.Add("data-val-country", ErrorMessage);
        }
    }

Index view for the UI

@model ASPNETCoreValidations.Models.Employee
@using ASPNETCoreValidations.Models.enums
@{
    ViewBag.Title = "Index";
}

<h1>Create</h1>

<h4>Business unit</h4>
<hr />
<div class="container">
    <form asp-action="Index">

        <div asp-validation-summary="All" class="text-danger"></div>

        <div class="form-group">
            <div class="row">
                <div class="col-md-6">
                    <label asp-for="Name" class="control-label"></label>
                    <input asp-for="Name" class="form-control" />
                    <span asp-validation-for="Name" class="text-danger"></span>
                </div>
                <div class="col-md-6">
                    <label asp-for="Email" class="control-label"></label>
                    <input asp-for="Email" class="form-control" />
                    <span asp-validation-for="Email" class="text-danger"></span>
                </div>
            </div>
        </div>


        <div class="form-group">
            <div class="row">
                <div class="col-md-6">
                    <label asp-for="Role" class="control-label"></label>
                    <select asp-for="Role" class="form-control" asp-items="Html.GetEnumSelectList<Roles>()">
                        <option value="">Select Department</option>
                    </select>
                    <span asp-validation-for="Role" class="text-danger"></span>
                </div>
                <div class="col-md-6">
                    <label asp-for="HireDate" class="control-label"></label>
                    <input asp-for="HireDate" class="form-control" />
                    <span asp-validation-for="HireDate" class="text-danger"></span>
                </div>
            </div>
        </div>

        <div class="form-group">
            <div class="row">
                <div class="col-md-6">
                    <label asp-for="Salary" class="control-label"></label>
                    <input asp-for="Salary" class="form-control" />
                    <span asp-validation-for="Salary" class="text-danger"></span>
                </div>
            </div>
        </div>

        <div class="form-group">
            <input type="submit" value="Create" class="btn btn-primary" />
        </div>
    </form>
</div>


@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
    <script src="~/js/RequiredIfValidate.js"></script>
}

Jquery for client-side validation

jQuery.validator.addMethod("requiredif",
    function (value, element, param) {    
        
      // I need help here. This method never gets executed and I don't know how to implement validation here ...
      // return true or false depending on the condition
    });

jQuery.validator.unobtrusive.adapters.addBool("requiredif");

Index action

        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Index(Employee employee)
        {
            if (ModelState.IsValid)
            {
                RedirectToAction("Index");
            }

            return View();
        }

Solution

  • According to your description, I suggest you could add a property in the RequiredIfAttribute AddValidation attribute to add the role to the input salary.

    Then I suggest you could try to use below validation unobtrusive scripts:

    RequiredIfAttribute :

    public class RequiredIfAttribute : ValidationAttribute, IClientModelValidator
    {
        public string PropertyName { get; set; }
        public object Value { get; set; }
    
        public RequiredIfAttribute(string propertyName, object value, string errorMessage = "")
        {
            PropertyName = propertyName;
            ErrorMessage = errorMessage;
            Value = value;
        }
    
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            var instance = validationContext.ObjectInstance;
            var type = instance.GetType();
            var proprtyvalue = type.GetProperty(PropertyName).GetValue(instance, null);
            if (proprtyvalue != null)
            {
                if (proprtyvalue.ToString() == Value.ToString() && value == null)
                {
                    return new ValidationResult(ErrorMessage);
                }
            }
            return ValidationResult.Success;
        }
    
        public void AddValidation(ClientModelValidationContext context)
        {
            context.Attributes.Add("data-val", "true");
            context.Attributes.Add("data-val-country", ErrorMessage);
            context.Attributes.Add("data-val-country-role", Value.ToString());
        }
    }
    

    Scripts:

    @section Scripts{
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.1/jquery.validate.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.min.js"></script>
    
        <script>
    
            $.validator.addMethod('country', function (value, element, params) {
                var genre = $(params[0]).val(), role = params[1], salar = value;
    
                var selecttest = $("#Role option:selected").text();
     
                if (selecttest == role) {
                    if (value.length == 0) {
                        console.log("selecttest == role value = null");
                        return false;
    
                    } else {
                        console.log("selecttest == role value != null");
                        return true;
                    }
                } else {
                    console.log("selecttest != role");
                    return true;
                }
    
    
    
            });
    
            $.validator.unobtrusive.adapters.add('country', ['role'], function (options) {
                var element = $(options.form).find('select#Salary')[0];
    
                options.rules['country'] = [element, options.params['role']];
                options.messages['country'] = options.message;
            });
        </script>
    
    }
    

    Result:

    enter image description here