this is part of “understanding Asp.net Mvc Unobtrusive Validation” series
- How the jQuery validate plugin works internally
- Understand the Html generated by the unobtrusive validation in Asp.net MVC
- How the unobtrusive jQuery validate plugin works internally in Asp.net MVC
What we will be talking about in this article
- The
parse
method parseElement
sectionskipAttach
parameter explanationparseElement
function explanationvalidateInfo
section]validateInfo
function explanation- The return object explanation
- Adapters
parse() method
we will explain the cycle of whats happening in the unobtrusive validation at the document load and will understand what is the role of every component
if we look at the end of the jquery.validate.unobtrusive.js
we will find
$(function () {
$jQval.unobtrusive.parse(document)
})
so we called the parse()
method and pass it document
so whats the parse method exactly
{
parse: function (selector) {
///
<summary>
/// Parses all the HTML elements in the specified selector. It looks for input elements decorated
/// with the [data-val=true] attribute value and enables validation according to the data-val-*
/// attribute values.
/// </summary>
///Any valid jQuery selector.
$(selector).find(":input[data-val=true]").each(function () {
$jQval.unobtrusive.parseElement(this, true);
});
var $forms = $(selector)
.parents("form")
.andSelf()
.add($(selector).find("form"))
.filter("form");
$forms.each(function () {
var info = validationInfo(this);
if (info) {
info.attachValidation();
}
});
}
}
There is two sections in the parse method
1- parseElement() section
parseElement(element, skipAttach)
$(selector)
.find(':input[data-val=true]')
.each(function () {
$jQval.unobtrusive.parseElement(this, true)
})
so the first thing that happens we iterate over all the elements that have a data-val=true inside the selector that we passed (its document in our case)
then call parseElement()
and pass it the element we want to validate and true
for skipAttach
skipAttach() parameter explanation
a question could come up why we passed true
to skipAttach
and not false
skipAttach
is a flag for calling validate()
on the form
If we passed false
it will translate the rules on the element that we have passed then immediately call validate on the rules array and pass along other options used by the unobtrusive validation. (if there is still other element to be parsed they wont)
We don’t want that. We first want to translate all the rules on every element in the form then after all the rules are translated we will call validate()
which is basically the second part of the parse method
So what other scenarios we would pass true
to skipAttach
?
If we want to add dynamic element to an already validated form we will pass true to skip validating the form again because it won’t do anything (we will talk about dynamic validating element in the next article)
parseElement() function
The parseElement()
does mainly two things
- On the first call on an element in a form (No element was called before it in the same form)
it will construct an object of options that will be passed to the validate()
method, the options are used by the jquery.unobtrusive
like custom errorPlacement
function, custom errorClass
and an empty rules array
Note: what is responsible of doing all that is a private method called
validationInfo(form)
that is called withinparseElement
and when its called more than 1 time it will just return the same object so we can add, modify or call data/functions in it
- For every element when we call
parseElement
it will understand the rules that are written on this element (thedata-*
) using theadapters
(Will explain later how the adapters works) and then translate and add them to the rules array that was constructed in the first call
every call to
parseElement
its result will be saved on the form itself using$.data(“unobtrusiveValidation“)
that’s how the separate calls sync in the same data source
2- validateInfo() section
var $forms = $(selector)
.parents('form')
.andSelf()
.add($(selector).find('form'))
.filter('form')
$forms.each(function () {
var info = validationInfo(this)
if (info) {
info.attachValidation()
}
})
validateInfo() function explanation
we already said calling validateInfo()
will construct an object of options for the validate()
method, the options are used by the jquery.unobtrusive
like a custom errorPlacement
function, custom errorClass
and an empty rules array
here we also called validationInfo()
for every form in the page basically at this point calling validationInfo()
we will only get the object stored on the form that was already populated in the first phase so we are using it as a getter
after that we are calling attachValidation()
which is basically calling the validate()
method passing it all the options populated by the validationInfo()
data_validation = 'unobtrusiveValidation'
function validationInfo(form) {
var $form = $(form),
result = $form.data(data_validation),
onResetProxy = $.proxy(onReset, form)
if (!result) {
result = {
options: {
// options structure passed to jQuery Validate’s validate() method
errorClass: 'input-validation-error',
errorElement: 'span',
errorPlacement: $.proxy(onError, form),
invalidHandler: $.proxy(onErrors, form),
messages: {},
rules: {},
success: $.proxy(onSuccess, form),
},
attachValidation: function () {
$form
.unbind('reset.' + data_validation, onResetProxy)
.bind('reset.' + data_validation, onResetProxy)
.validate(this.options)
},
validate: function () {
// a validation function that is called by unobtrusive Ajax
$form.validate()
return $form.valid()
},
}
$form.data(data_validation, result)
}
return result
}
first we are checking if we already called this function on the form before by using $form.data(“unobtrusiveValidation“)
if we did then do nothing and return the result
The return object explanation
If its the first time we call validationInfo()
then we construct a result object and will save it on the form using $.data()
method this object will contain 3 parts:
- An object which is the basically all the options that we will pass to the
validate()
method with an empty rules & messages arrays that will be constructed later attachValidation()
method will bind a custom event to the form itself“reset.unobtrusiveValidation”
and after call validate on the method with all the options , this method will be called when the rules & messages arrays are completed (triggering the custom"reset"
event will callonReset()
method which will basically resets everything)
function onReset(event) {
// ‘this’ is the form element
var $form = $(this)
$form.data('validator').resetForm()
$form.find('.control-group').removeClass('error')
$form
.find('.validation-summary-errors')
.addClass('validation-summary-valid')
.removeClass('validation-summary-errors')
$form
.find('.field-validation-error')
.addClass('field-validation-valid')
.removeClass('field-validation-error')
.removeData('unobtrusiveContainer')
.find('>*') // If we were using valmsg-replace, get the underlying error
.removeData('unobtrusiveContainer')
}
so if we want to trigger the reset event to reset the form
$(‘form’).trigger(‘reset.unobtrusiveValidation’)
- a custom
validate()
method that will be called from unobtrusive ajax
Adapters
I intentionally left the adapters
section out when i talked about the parseElement()
method because its complicated enough to be in a sub-section
We looked at how Html is generated using unobtrusive validation and how to add custom validation attribute in normal jquery.validate
what links the two is the Adapters
So What is the adapter’s responsibility ?
it is responsible for translating the Html data-*
to a format that can be understood by the normal jquery.validate
If a user want to add a custom validation method using the unobtrusive validation he must also provide an adapter for it
the adapters
collection resides in *$.validator.unobtrusive.adapters
- the
adapters
collection consist of all the default adapters defined by default injquery.unobstrusive
and the ones that the user has defined - It also contains 4 methods for adding custom adapters that we will take a look at later
so lets look at the most generic method which is
jQuery.validator.unobtrusive.adapters.add(adapterName, [params], fn)
you can consider this method the $.ajax
method and the other three are helper methods that uses it
so lets explain the parameters
-
adapterName
: is the adapter name as the name implies , and it matches the ruleName in the Html elementdata-val-ruleName
-
[params]
: an optional parameter array that the validation method would use to complete validation -
fn
: Is called to map the Htmldata-*
to rules and messages used by thevalidate()
method and it has a parameter option passed to it which is an object containing the following properties: -
element
: the Html element being validated -
form
: the form element -
message
: the error message for this rule extracted fromdata-*
attribute on the element -
params
: parameters that are used for the validation and its an array extracted from thedata-*
attributes on the Html elementdata-val-ruleName-param1
-
rules
: The jquery rules array for this element , your expected to add to this array the rule(s) that this adapter is used for you will pass key/value pairs -
the key is the validation rule name ,
-
the value is the parameters used for this rule (check this section in that article for adding custom rules to
jquery.validate
) -
messages
: The jquery messages array for this element, same as the rules object you are expected to fill it and its used as the messages object for this Html element in the validate method
There is no return result from the method. Whats happening manipulating the rules & messages arrays will directly be saved on the form itself using
$.data(“unobtrusiveValidation")
you can check theparseElement
method for the details of how the parameter where passed to the adapter function
Example:
<input
id="val"
type="text"
name="val"
data-val="true"
data-val-between="Must be in the right range"
data-val-between-min="5"
data-val-between-max="30"
/>
//The adapter
jQuery.validator.unobtrusive.adapters.add(
‘between’, [‘min’ ,’max’],
function (options) {
options.rules[‘between’] = {
min: options.params.min,
max: options.params.max
};
options.messages[‘between’] = options.message;
}
);
//The validation method
jQuery.validator.addMethod("between", function (value, element, params) {
params.min == 5; //true
params.max == 30; //true
});
so what about the other adapters
addBool
addSingleVal
addMinMax
They are all pretty simple if you understood the concept of adding a custom adapter using the add()
method explained before
You can check Brad Wilson’s article he explained in it the adapters in depth