Partial forms validation in ASP.NET MVC 2 (something like WebForms ValidationGroup)
In one of my recent ASP.NET MVC 2 projects I had to make a form divided into few tabs. Each tab had to be validated before user can move to next one. Unfortunately ASP.NET MVC 2 built in validation doesn't provide mechanism for this. Well we are developers, so if there is no built in mechanism, we should create our own. First lets create a ValidationAttribute, which will be used to add information about validation group:
We will also need a DataAnnotationsModelValidator for this attribute:
Don't forget to register above classes in your Global.asax:
So this is all we need on server side (please remember, that we don't want to achieve any server side logic, only partial validation on client side before posting the whole form). To make it work on client side, first we need to modify Sys.Mvc.FormContext._parseJsonOptions in MicrosoftMvcValidation.debug.js (or corresponding part in MicrosoftMvcValidation.js):
Now we can write ourselves function, which will perform partial validation based on group name:
And our job is done. Now we can call Sys.Mvc.FormContext.validateGroup('formId', 'validationGroup'); whenever we want to perform partial validation. I have created a sample application which make use of those modification, you can download it from my repository. If there is a need for same modification for jQuery validation, let me know and I will look into it.
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class ValidationGroupAttribute : ValidationAttribute
{
#region Properties
/// <summary>
/// Name of the validation group
/// </summary>
public string GroupName { get; set; }
#endregion
#region Constructor
public ValidationGroupAttribute(string groupName)
{
GroupName = groupName;
}
#endregion
#region Methods
public override bool IsValid(object value)
{
//No validation logic, always return true
return true;
}
#endregion
}We will also need a DataAnnotationsModelValidator for this attribute:
public class ValidationGroupValidator : DataAnnotationsModelValidator<ValidationGroupAttribute>
{
#region Fields
string _groupName;
#endregion
#region Constructor
public ValidationGroupValidator(ModelMetadata metadata, ControllerContext context, ValidationGroupAttribute attribute)
: base(metadata, context, attribute)
{
_groupName = attribute.GroupName;
}
#endregion
#region Methods
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
{
//Add informations about validation group to metadata
var validatioRule = new ModelClientValidationRule
{
ErrorMessage = String.Empty,
ValidationType = "validationGroup"
};
validatioRule.ValidationParameters.Add("groupName", _groupName);
return new[] { validatioRule };
}
#endregion
}Don't forget to register above classes in your Global.asax:
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(ValidationGroupAttribute), typeof(ValidationGroupValidator));
So this is all we need on server side (please remember, that we don't want to achieve any server side logic, only partial validation on client side before posting the whole form). To make it work on client side, first we need to modify Sys.Mvc.FormContext._parseJsonOptions in MicrosoftMvcValidation.debug.js (or corresponding part in MicrosoftMvcValidation.js):
Sys.Mvc.FormContext._parseJsonOptions = function Sys_Mvc_FormContext$_parseJsonOptions(options) {
var formElement = $get(options.FormId);
var validationSummaryElement = (!Sys.Mvc._validationUtil.stringIsNullOrEmpty(options.ValidationSummaryId)) ? $get(options.ValidationSummaryId) : null;
var formContext = new Sys.Mvc.FormContext(formElement, validationSummaryElement);
formContext.enableDynamicValidation();
formContext.replaceValidationSummary = options.ReplaceValidationSummary;
for (var i = 0; i < options.Fields.length; i++) {
var field = options.Fields[i];
var fieldElements = Sys.Mvc.FormContext._getFormElementsWithName(formElement, field.FieldName);
var validationMessageElement = (!Sys.Mvc._validationUtil.stringIsNullOrEmpty(field.ValidationMessageId)) ? $get(field.ValidationMessageId) : null;
var fieldContext = new Sys.Mvc.FieldContext(formContext);
Array.addRange(fieldContext.elements, fieldElements);
fieldContext.validationMessageElement = validationMessageElement;
fieldContext.replaceValidationMessageContents = field.ReplaceValidationMessageContents;
for (var j = 0; j < field.ValidationRules.length; j++) {
var rule = field.ValidationRules[j];
//Here goes our small modification
if (rule.ValidationType == 'validationGroup') {
fieldContext.validationGroup = rule.ValidationParameters['groupName'];
}
else {
var validator = Sys.Mvc.ValidatorRegistry.getValidator(rule);
if (validator) {
var validation = Sys.Mvc.$create_Validation();
validation.fieldErrorMessage = rule.ErrorMessage;
validation.validator = validator;
Array.add(fieldContext.validations, validation);
}
}
}
fieldContext.enableDynamicValidation();
Array.add(formContext.fields, fieldContext);
}
var registeredValidatorCallbacks = formElement.validationCallbacks;
if (!registeredValidatorCallbacks) {
registeredValidatorCallbacks = [];
formElement.validationCallbacks = registeredValidatorCallbacks;
}
registeredValidatorCallbacks.push(Function.createDelegate(null, function () {
return Sys.Mvc._validationUtil.arrayIsNullOrEmpty(formContext.validate('submit'));
}));
return formContext;
}Now we can write ourselves function, which will perform partial validation based on group name:
Sys.Mvc.FormContext.validateGroup = function Sys_Mvc_FormContext$validateGroup(formId, groupName) {
//Get form element
var formElement = $get(formId);
//Get form context
var formContext = Sys.Mvc.FormContext.getValidationForForm(formElement);
//Get form fields
var fields = formContext.fields;
//Array for errors
var errors = [];
//For each field
for (var i = 0; i < fields.length; i++) {
var field = fields[i];
//If field has validation group and its name matches the one we are looking for
if (field.validationGroup && field.validationGroup == groupName) {
//Validate field
var fieldErrors = field.validate('submit');
if (fieldErrors) {
Array.addRange(errors, fieldErrors);
}
}
}
//Return true it there are no errors, otherwise false
return (!errors || !errors.length);
}And our job is done. Now we can call Sys.Mvc.FormContext.validateGroup('formId', 'validationGroup'); whenever we want to perform partial validation. I have created a sample application which make use of those modification, you can download it from my repository. If there is a need for same modification for jQuery validation, let me know and I will look into it.