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?
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;
});