Search code examples
c#asp.netasp.net-mvcvalidationunobtrusive-validation

ASP.NET MVC Custom Validation with Unobtrusive


I am using ASP.NET MVC with Unobtrusive js and it's running perfectly fine but I want to set a rule of Validation for something like this:

I have dropdownlist with two values "Yes" and "No". I have one textbox as well.

Based on selection of the values from dropdownlist if

"Yes" I want to validate that textbox value entered or not using Unobtrusive at client side with server side as well and if

"No" than no validation should work I mean it's ok if textbox value blank or whatever the values entered because I don't want to store that value if dropdownlist value is No no validation should work on that textbox.

Please someone help me out with this problem.

I have already refer so many stackoverflow questions and answers it's pretty good answers too there as well but I am not able to put those to get my solution.

I got the values of dropdown selection in bool i.e.

 public bool IsTestUnit { get; set; }
    [RequiredIfUniTestIsYes("IsTestUnit == true", true, ErrorMessage = "Please Enter Test Unit Job Order Number ")]
        public long TestUnitJobOrder { get; set; }

The code that I have tried:

 public class RequiredIfUniTestIsYes : ValidationAttribute, IClientValidatable
    {
        public RequiredIfUniTestIsYes()
        {

        }

        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            var countryPropertyInfo = validationContext.ObjectInstance.GetType().GetProperty("IsTestUnit");
            var countryValue = countryPropertyInfo.GetValue(validationContext.ObjectInstance, null).ToString();
            if (countryValue == "True" && (value.ToString().Trim() == "" || Convert.ToInt32(value) == 0))
            {
                return new ValidationResult("Please Enter Unit Test Job Order Number");
            }

            return ValidationResult.Success;
        }

        public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
        {
            var rule = new ModelClientValidationRule();
            rule.ErrorMessage = "Please Enter Unit Test Job Order Number";
            rule.ValidationParameters.Add("istestunit", "true");
            rule.ValidationType = "isunittest";

            yield return rule;
        }
    }

Javascript:

$.validator.addMethod("isunittest", function (state, element, country) {
                 var country = $('#WorkOderDetailViewModel_IsTestUnit').val();
                     if (country == 'True' && state == '') {
                         return false;
                     }
                     return true;
                 });

             $.validator.unobtrusive.adapters.addSingleVal("isunittest", "true");

             (function ($) {
                 $.validator.addMethod('customRequire', function (value, element) {
                     // your validation logic here
                     return true; // true if valid, otherwise false 
                 });
                 $.validator.unobtrusive.adapters.add('customRequire');
             })(jQuery);

I have just copy paste the code and do not change the name so please ignore those mistake.


Solution

  • Given that you render the fields as below:

    @Html.DropDownListFor(m => m.IsTestUnit,
    new[] { true, false }.Select(b => new SelectListItem { Text = b ? "Yes" : "No", Value = b.ToString().ToLower() }),
    new { id = "WorkOderDetailViewModel_IsTestUnit" })
    
    @Html.TextBoxFor(m => m.TestUnitJobOrder)
    @Html.ValidationMessageFor(m => m.TestUnitJobOrder)
    

    with the model properties declared as:

        public bool IsTestUnit { get; set; }
    
        [RequiredIfUniTestIsYes("IsTestUnit", ErrorMessage = "Please Enter Test Unit Job Order Number ")]
        public long? TestUnitJobOrder { get; set; }
    

    You can define a custom validator like this:

    public class RequiredIfUniTestIsYes : ValidationAttribute, IClientValidatable
    {
        private string _dependency;
    
        public RequiredIfUniTestIsYes(string dependency)
        {
            _dependency = dependency;
        }
    
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            var countryPropertyInfo = validationContext.ObjectInstance.GetType().GetProperty(_dependency);
            var countryValue = (bool)countryPropertyInfo.GetValue(validationContext.ObjectInstance, null);
            var number = default(long);
            if (countryValue && (value == null || !long.TryParse(value.ToString(), out number)))
            {
                return new ValidationResult("Please Enter Unit Test Job Order Number");
            }
    
            return ValidationResult.Success;
        }
    
        public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
        {
            var rule = new ModelClientValidationRule();
            rule.ErrorMessage = "Please Enter Unit Test Job Order Number";
            rule.ValidationParameters.Add("istestunit", "true");
            rule.ValidationType = "isunittest";
    
            yield return rule;
        }
    }
    

    And register client-side unobtrusive validation like this:

        $.validator.unobtrusive.adapters.add("isunittest", ['istestunit'], function(options){
            options.rules['isunittest'] = options.params;
            options.messages['isunittest'] = options.message;
        });    
        $.validator.addMethod('isunittest', function (state, element, params) {
            var country = $('#WorkOderDetailViewModel_IsTestUnit').val();
            if (country == 'true' && state == '') {
                return false;
            }
            return true;
        });