ASP.NET MVC for checking multiple fields

I am developing an ASP.NET MVC 5.2.3 user data annotation for validation in Visual Studio 2015. It needs to accept any number of fields and ensure that if you have a value, they should all have a value; if they are all empty / empty, everything should be in order.

A few examples helped:

However, I am not sure how to perform client-side validation, where you check an unknown number of fields.

How to pass this to the client using the implementation of the GetClientValidationRules() method of the GetClientValidationRules() interface?

Also, how to apply this new data annotation to the properties of my view model? Would it look like this?

 [MultipleRequired("AppNumber", "UserId", /* more fields */), ErrorMessage = "Something..."] [DisplayName("App #")] public int AppNumber { get; set; } [DisplayName("User ID")] public int UserId { get; set; } 

Here, as far as I could get with the MultipleRequiredAttribute user data annotation class:

 public class MultipleRequiredAttribute : ValidationAttribute, IClientValidatable { private readonly string[] _fields; public MultipleRequiredAttribute(params string[] fields) { _fields = fields; } protected override ValidationResult IsValid(object value, ValidationContext validationContext) { // If any field has value, then all must have value var anyHasValue = _fields.Any(f => !string.IsNullOrEmpty(f)); if (!anyHasValue) return null; foreach (var field in _fields) { var property = validationContext.ObjectType.GetProperty(field); if (property == null) return new ValidationResult($"Property '{field}' is undefined."); var fieldValue = property.GetValue(validationContext.ObjectInstance, null); if (string.IsNullOrEmpty(fieldValue?.ToString())) return new ValidationResult(FormatErrorMessage(validationContext.DisplayName)); } return null; } public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context) { yield return new ModelClientValidationRule { ErrorMessage = ErrorMessage, ValidationType = "multiplerequired" }; } } 

Thanks.

+3
source share
1 answer

To obtain client-side validation, you need to pass the values ​​of the “other properties” to the ModelClientValidationRule using the .Add() method of the ValidationParameters rule property, and then write client-side scripts to add the rules to $.validator .

But first, there are a few other issues related to your attribute. First, you should run the foreach only if the value of the property to which you applied the attribute is null . Secondly, returning a ValidationResult if one of the “other properties” does not exist is confusing and pointless for the user, and you should just ignore it.

The attribute code should be (note that I changed the attribute name)

 public class RequiredIfAnyAttribute : ValidationAttribute, IClientValidatable { private readonly string[] _otherProperties; private const string _DefaultErrorMessage = "The {0} field is required"; public RequiredIfAnyAttribute(params string[] otherProperties) { if (otherProperties.Length == 0) // would not make sense { throw new ArgumentException("At least one other property name must be provided"); } _otherProperties = otherProperties; ErrorMessage = _DefaultErrorMessage; } protected override ValidationResult IsValid(object value, ValidationContext validationContext) { if (value == null) // no point checking if it has a value { foreach (string property in _otherProperties) { var propertyName = validationContext.ObjectType.GetProperty(property); if (propertyName == null) { continue; } var propertyValue = propertyName.GetValue(validationContext.ObjectInstance, null); if (propertyValue != null) { return new ValidationResult(FormatErrorMessage(validationContext.DisplayName)); } } } return ValidationResult.Success; } public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context) { var rule = new ModelClientValidationRule { ValidationType = "requiredifany", ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()), }; / pass a comma separated list of the other propeties rule.ValidationParameters.Add("otherproperties", string.Join(",", _otherProperties)); yield return rule; } } 

Then the scripts will be

 sandtrapValidation = { getDependentElement: function (validationElement, dependentProperty) { var dependentElement = $('#' + dependentProperty); if (dependentElement.length === 1) { return dependentElement; } var name = validationElement.name; var index = name.lastIndexOf(".") + 1; var id = (name.substr(0, index) + dependentProperty).replace(/[\.\[\]]/g, "_"); dependentElement = $('#' + id); if (dependentElement.length === 1) { return dependentElement; } // Try using the name attribute name = (name.substr(0, index) + dependentProperty); dependentElement = $('[name="' + name + '"]'); if (dependentElement.length > 0) { return dependentElement.first(); } return null; } } $.validator.unobtrusive.adapters.add("requiredifany", ["otherproperties"], function (options) { var element = options.element; var otherNames = options.params.otherproperties.split(','); var otherProperties = []; $.each(otherNames, function (index, item) { otherProperties.push(sandtrapValidation.getDependentElement(element, item)) }); options.rules['requiredifany'] = { otherproperties: otherProperties }; options.messages['requiredifany'] = options.message; }); $.validator.addMethod("requiredifany", function (value, element, params) { if ($(element).val() != '') { // The element has a value so its OK return true; } var isValid = true; $.each(params.otherproperties, function (index, item) { if ($(this).val() != '') { isValid = false; } }); return isValid; }); 
+1
source

All Articles