Search code examples
c#asp.net-mvcunobtrusive-validation

Get full property name for MVC client validation for list of objects


I am writing a custom MVC validation attribute that is dependent on another named property within the model. Implementing IClientValidatable my code looks like this:

public IEnumerable<ModelClientValidationRule> GetClientValidationRules
    (ModelMetadata metadata, ControllerContext context)
    {            
        var name = metadata.DisplayName ?? metadata.PropertyName;
        ModelClientValidationRule rule = new ModelClientValidationRule()
        { ErrorMessage = FormatErrorMessage(name), ValidationType = "mycustomvalidation" };
        rule.ValidationParameters.Add("dependentproperty", dependentProperty);

        yield return rule;
    }

The trouble is that I am trying to use this in a list of elements. The dependent property is rendered in the view with a name of MyListOfObjects[0].DependentProperty and the validation rule is rendered as data-val-mycustomvalidation-dependentproperty="DependentProperty"

How do I access the full name of the dependent property from within GetClientValidationRules(ModelMetadata metadata, ControllerContext context)so that it renders as data-val-mycustomvalidation-dependentproperty="MyListOfObjects[0].DependentProperty"

The model looks like this:

public class MyClass
{
    public string SomeValue { get; set; }
    public List<Item> MyListOfObjects  { get; set; }

    public class Item
    {
        [MyCustomValidation("DependentProperty")]
        public int MyValidatedElement  { get; set; }

        public int DependentProperty  { get; set; }

    }
}  

Solution

  • You do not need the fully qualified property name in the validation attribute, and you cannot determine it in any case because the validation context is (in your case) typeof Item (the attribute has no context of of the parent MyClass).

    Where you do need the full name is in the client side script (when you add the adapter to $.validator.unobtrusive. The following script will return the idattribute of the dependent property

    myValidationNamespace = {
      getDependantProperyID: function (validationElement, dependantProperty) {
        if (document.getElementById(dependantProperty)) {
          return dependantProperty;
        }
        var name = validationElement.name;
        var index = name.lastIndexOf(".") + 1;
        dependantProperty = (name.substr(0, index) + dependantProperty).replace(/[\.\[\]]/g, "_");
        if (document.getElementById(dependantProperty)) {
            return dependantProperty;
        }
        return null;
      }
    }
    

    You can then use it when initializing client side validation

    $.validator.unobtrusive.adapters.add("mycustomvalidation", ["dependentproperty"], function (options) {
      var element = options.element;
      var dependentproperty = options.params.dependentproperty;
      dependentproperty = myValidationNamespace.getDependantProperyID(element, dependentproperty);
      options.rules['mycustomvalidation'] = {
        dependentproperty: dependentproperty
      };
      options.messages['mycustomvalidation'] = options.message;
    });