Search code examples
c#jqueryasp.net-mvcvalidationunobtrusive-validation

Is it possible to add client validation to class level validator for MVC C#?


I have nested model with a custom validator that validates child elements. This approach works great for the sever side, however, I would also like to add client side support.

Here is my Model:

public class Customer
    {
        public string Name { get; set; } = "";

        [AtLeastOneProperty("HomePhone", "WorkPhone", "CellPhone", ErrorMessage = "At least one phone number is required for Customer")]
        public Phone phone { get; set; }

        public Customer()
        {
            phone = new Phone();
        }
    }
     public class Phone
     {
        public string HomePhone { get; set; } = "";

        public string WorkPhone { get; set; } = "";

        public string WorkExt { get; set; } = "";

        public string CellPhone { get; set; } = "";
     }

Here is my Validator:

public class AtLeastOnePropertyAttribute : ValidationAttribute, IClientValidatable
    {
        private readonly string[] _properties;
        public AtLeastOnePropertyAttribute(params string[] properties)
        {
            _properties = properties;
        }

        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            if (_properties == null || _properties.Length < 1)
            {
                return null;
            }
            PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value);

            List<string> props = new List<string>();
            props.Add(properties[0].ToString());
            foreach (var property in _properties)
            {

                validationContext = new ValidationContext(value);

                var propertyInfo = validationContext.ObjectType.GetProperty(property);
                if (propertyInfo == null)
                {
                    return new ValidationResult(string.Format("unknown property {0}", property));
                }

                var propertyValue = propertyInfo.GetValue(validationContext.ObjectInstance, null);
                if (propertyValue is string && !string.IsNullOrEmpty(propertyValue as string))
                {
                    return null;
                }

                if (propertyValue != null)
                {
                    return null;
                }
            }

            return new ValidationResult(FormatErrorMessage(validationContext.DisplayName), props);
        }

        public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
        {
            var rule = new ModelClientValidationRule
            {
                ErrorMessage = ErrorMessage,
                ValidationType = "atleastonerequired"
            };
            rule.ValidationParameters["properties"] = string.Join(",", _properties);

            yield return rule;
        }
    }

This code works for server side validation, however, I cannot get it to build Client side validation unobtrusive tags for those properties that I am checking. Right now it is trying to build the tags for the class level Phone because the attribute tag is above a nested model property instead of an individual property.

Is it even possible to make it add client side tags? Maybe making an Validation adapter?


Solution

  • I have previously used Expressive Annotations for the same. They allow us to specify attributes with Boolean conditions which can be triggered at both client side and server side.