13

This is more of a theoretical question.

I'm currently examining the MVC 3 validation by using ComponentModel.DataAnnotations, and everything works automagically, especially on client side.

Somehow something checks for those attributes, and generates javascript for the validation (or html5 attributes, if using unobtrusive mode), and it works.

My question is that what generates the client side javascript and how can I access and modify it? For example I want to handle the given dataannotation attributes a little differently, or handle custom attributes (I have found that I can derive them from ValidationAttribute, but maybe for some reason I don't want).

Can someone explain it to me what really happens? (Or links to good explanations would also be good, as I have only found tutorials for actually using dataannotations)

EDIT: Also with deriving from ValidationAttribute, the client-side validation is not working automatically. Why?

vinczemarton
  • 7,756
  • 6
  • 54
  • 86

1 Answers1

15

MVC3 has a new jQuery Validation mechanism that link jQuery Validation and Validation Attributes Metadata, this is the jquery.validate.unobtrusive file that takes all data- attributes and work with them, just like before when you set the

<add key="UnobtrusiveJavaScriptEnabled" value="false" />

All you need to do is come up with your own Custom Validation Attributes, for that you have 2 options:

  • Create a Custom Validation Attribute that inherits the ValidationAttribute interface and override the IsValid

or

  • Create a Self Validate Model use the model IValidatebleObject that all you need is to return the Validate method

in MVC3 you now have a method that you can override that has a ValidationContext object, where you can simply get all references, properties and values of any other object in the form

Create your own, and that unobtrusive file will handle the mapping of what your custom validator needs and will work out together with the jQuery Validation plugin.

YOU DO NOT Change the javascript... that's sooo 90's and not MVC way!

for example if you want to validate, let's say 2 dates that the last can not be less than the first (period of time for example)

public class TimeCard
{
    public DateTime StartDate { get; set; }

    [GreaterThanDateAttribute("StartDate")]
    public DateTime EndDate { get; set; }
}

creating a Custom Validation

public class GreaterThanDateAttribute : ValidationAttribute
{
    public string GreaterThanDateAttribute(string otherPropertyName)
        :base("{0} must be greater than {1}")
    {
        OtherPropertyName = otherPropertyName;
    }

    public override string FormatErrorMessage(string name)
    {
        return String.Format(ErrorMessageString, name, OtherPropertyName);
    }

    public override ValidateionResult IsValid(object value, ValidationContext validationContext)
    {
        var otherPropertyInfo = validationContext.ObjectTYpe.GetProperty(OtherPropertyName);
        var otherDate = (DateTime)otherPropertyInfo.GetValue(validationContext.ObjectInstance, null);
        var thisDate = (DateTime)value;

        if( thisDate <= otherDate )
        {
            var message = FormatErrorMessage(validationContext.DisplayName);
            return new ValidationResult(message);
        }

        return null;        
    }    
}

if using the Self Validating model then the code would be just

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
    if( EndDate <= StartDate )
        yield return new ValidationResult("EndDate must be grater than StartDate");
}

Keep in mind that the Custom Validation is Generic, that's why much code, and Self Validating Model only works on the model applied.

Hope it helps


added

I didn't explain the Custom Client Validation part, fell free to ask if you need examples, but basically:

It's easier in MVC3 (if of course, you understand jQuery.Validate) all you need to do is:

  • Implement IClientValidateble
  • Implement a jQuery validation method
  • Implement an unobtrusive adapter

To create this 3 things, let's take this GreaterThanDateAttribute into account and create the Custom Client Side Validation. For that we need to code this:

append to the GreaterThanDateAttribute

public IEnumerable<ModelCLientValidation> GetCLientValidationRules(ModelMetadata metadata, ControllerContext context)
{
    var rule = new ModelCLientValidationRule();
    rule.ErrorMessage = FormatErrorMessage(metadata.GetDisplayName());
    rule.ValidationType = "greater"; // This is what the jQuery.Validation expects
    rule.ValidationParameters.Add("other", OtherPropertyName); // This is the 2nd parameter

    yield return rule;
}

Then you need to write the new jQuery Validator and the metadata adapter that will link the jQuery.Validation with your code providing the correct data- attributes for that field (if of course, UnobtrusiveJavaScriptEnabled is true)

create a new js file and attach to your <head> for example as

<script src="@Url.Content("~/Scripts/customValidation.js")" type="text/javascript"></script>

and append the new validation

jQuery.validator.addMethod("greater", function(value, element, param) {
    // we need to take value and compare with the value in 2nd parameter that is hold in param
    return Date.parse(value) > Date.parse($(param).val());
});

and then we write the adapter

jQuery.validator.unobtrusive.adapters.add("greater", ["other"], function(options) {
    // pass the 'other' property value to the jQuery Validator
    options.rules["greater"] = "#" + options.param.other;
    // when this rule fails, show message that comes from ErrorMessage
    options.messages["greater"] = options.message;
});
balexandre
  • 73,608
  • 45
  • 233
  • 342
  • Thanks for your answer. The self validating model was a new approach to me. But you misunderstood the question. I was not interested in actually creating the custom validation attributes, but interested in the way the javascript (or html5 attributes, if unobtrusive mode is selected) gets generated from the validation attributes. Sorry that I forced in a childish example for how this knowledge would be useful, but that really wasn't the point of my post. This aside your answer was really good. – vinczemarton Mar 01 '11 at 13:53
  • 1
    I'm sorry, but I'm not following. You want to know how the `data-` is generated? that is the work of the helper `Html.TextBoxFor()` method, as it knows already all the Data Anotations that field needs, and if Unobstrutive setting is true it will generate all `data-` that he needs, then the Adapter links that into jQuery.Validate plugin. Fell free to ask for examples, I can great all this for the example above of the `GreaterThanDateAttribute` maybe it helps you understand it better? – balexandre Mar 01 '11 at 14:03
  • added Custom Client Validation example, create it and play with it so you can see what the `Custom Client Validation` does. It will add to that `` the attributes `data-val` and `data-val-greater` and well all required attributes to perform client side validation. **Does** this last example answer your question? – balexandre Mar 01 '11 at 14:20
  • Thanks for your answers, I have made a followup question for this, I would be glad if you could help me on that one too :D http://stackoverflow.com/questions/5155816/clientside-validation-in-self-validate-model-in-asp-net-mvc3 – vinczemarton Mar 01 '11 at 14:27
  • you have all the code for that answer right here, after the division line. What more do you need? Client Validation is the same for both parts, the Self validation is Server Side Validation. – balexandre Mar 01 '11 at 14:32