Rails 3 Client Side Validations 工作机制备忘

最近看了一下 [url=https://github.com/bcardarella/client_side_validations]Client Side Validations[/url] 这个插件 JavaScript 部分的源码,记录一下对它的理解(版本为 Rails 3 Client Side Validations - v3.1.0 )。

[b]一、客户端校验的设置信息[/b]
首先,来看一下它是通过什么方式在客户端得到校验规则的。在页面渲染的时候,client_side_validations 会在 FormBuilder 输出的表单 HTML 后边附加一段 JavaScript,内容看起来像是这样:

window['new_article'] = {
"type": "SimpleForm::FormBuilder",
"error_class": ["error"],
"error_tag": "span",
"wrapper_error_class": "field_with_errors",
"wrapper_tag": "div",
"validators": {
"article[title]": {
"presence": {
"message": "不能为空"
},
"length": {
"messages": {
"minimum": "过短",
"maximum": "过长"
},
"minimum": 2,
"maximum": 100
}
}
}
};

这段 JavaScript 使用了表单 id 作为一个全局变量的名字(本文中为 new_article),来存储这个表单的校验配置信息。下面,我先解释一下这个全局变量的属性:
[list=1]
[*] [b]type[/b] 属性的值,为 FormBuilder 类型, 它与表单中显示、隐藏错误信息的操作有关,后面会讲到
[*] [b]error_tag[/b] 和 [b]error_class[/b] 为错误信息容器的标签和 CSS 类
[*] [b]wrapper_tag[/b] 和 [b]wrapper_error_class[/b] 为整个表单项的容器的标签和 CSS 类
[*] [b]validators[/b] 是整个插件的关键,,是根据我们在 Rails 的 Model 中设置的校验规则生成的。validators 的第一层属性是各个需要校验的表单项的 name(比如 <input name="article[title]" > ),而这些属性对应的值,则表示这个表单项所需要进行的校验及校验所需的参数和出错信息。
[/list]
在 client_side_validations 的源码中,把前边说的这个全局变量称为 settings,下面我为了描述方便,也这么叫它。所以在本文中我说到 settings 时请想到 window['new_article']。

[b]二、client_side_validations 中的 validators[/b]
在说到整个插件的工作流程之前,我们先把前边说的 settings 中的 validators 属性和 client_side_validations 源码中的校验函数对应起来看一下:

var clientSideValidations = {
validators: {
all: function() { return jQuery.extend({}, clientSideValidations.validators.local, clientSideValidations.validators.remote) },
local: {
presence: function(element, options) { /* ... */ },
acceptance: function(element, options) { /* ... */ },
format: function(element, options) { /* ... */ },
numericality: function(element, options) { /* ... */ },
length: function(element, options) { /* ... */ },
exclusion: function(element, options) { /* ... */ },
inclusion: function(element, options) { /* ... */ },
confirmation: function(element, options) { /* ... */ }
},
remote: {
uniqueness: function(element, options) { /* ... */ }
}
},
/* formBuilders、callbacks 部分代码 */
}

我把每个函数的的具体实现去掉了,这样就可以很容易的看出 settings 的 validators 所包含的,就是此处这些校验函数的名字。没错,在执行校验的时候,就是通过 settings 中的那些名字来找到这些函数并进行调用的。

[b]三、client_side_validations 中的 formBuilders[/b]

var clientSideValidations = {
/* validators 部分代码 */,
formBuilders: {
'ActionView::Helpers::FormBuilder': {
add: function(element, settings, message) { /* ... */ },
remove: function(element, settings) { /* ... */ }
},
'SimpleForm::FormBuilder': {
add: function(element, settings, message) { /* ... */ },
remove: function(element, settings) { /* ... */ }
},
'Formtastic::FormBuilder': {
add: function(element, settings, message) { /* ... */ },
remove: function(element, settings) { /* ... */ }
},
'NestedForm::Builder': {
add: function(element, settings, message) { /* ... */ },
remove: function(element, settings, message) { /* ... */ }
}
},
/* callbacks 部分代码 */
}

在这个地方只做两件事——在页面中显示错误信息(add 函数)和去掉错误信息(remove 函数)。这几对 add、remove 函数只是在不同的 FormBuilder 下的不同实现而已。在表单项校验完毕时,根据 settings 的 type 属性来调用这里对应的 add() 或者 remove()。

[b]四、client_side_validations 中的 callbacks[/b]

var clientSideValidations = {
/* validators 和 formBuilders 部分代码 */,
callbacks: {
element: {
after: function(element, eventData) { },
before: function(element, eventData) { },
fail: function(element, message, addError, eventData) { addError() },
pass: function(element, removeError, eventData) { removeError() }
},
form: {
after: function(form, eventData) { },
before: function(form, eventData) { },
fail: function(form, eventData) { },
pass: function(form, eventData) { }
}
}
}

这个部分的代码,我没做删减,可以看出 client_side_validations 只使用了两个 callback,其余都是预留的。如果想设置 callback 可以这样做:

jQuery(function () {
clientSideValidations.callbacks.element.before = function(element, eventData) {
/* your code */
};
});


[b]五、整体流程和 $.fn.validate 等主要函数 [/b]
有了前面几个部分的铺垫,client_side_validations 的工作流程就好理解多了,下面我把对应的源码细节替换成注释,来大概理顺一下代码:

(function($) {
$.fn.validate = function() {
return this.filter('form[data-validate]').each(function() {
var form = $(this);
var settings = window[form.attr('id')];
// $.fn.validate() 的作用是为表单和表单项的一些事件绑定 callback
// 包括前边说到的那8个 callbacks
// 其中 element:validate:fail 事件触发时会调用前边提到的 formBuilders 中的 add() 函数
// 而 element:validate:pass 事件则对应调用 formBuilders 中的 remove() 函数
// 当表单的 submit 和 ajax:beforeSend 事件触发时,对此表单调用isValid(settings.validators)
// 当需要校验的表单项(除radio)触发 focusout 时,对此表单项调用 isValid(参数同上)
// 当需要校验的 checkbox 触发 click 时,对此表单项调用 isValid(参数同上)
// 当 _confirmation 这种匹配项,触发 focusout、keyup 事件时,对此表单项调用isValid(参数同上)
});
}

$.fn.isValid = function(validators) {
if ($(this[0]).is('form')) {
return validateForm($(this[0]), validators);
} else {
return validateElement($(this[0]), validators[this[0].name]);
}
}

var validateForm = function(form, validators) {
// 触发 form:validate:before 事件
// 对表单中每一个需要校验的表单项调用 isValid(validators)
// 触发 form:validate:pass 或 form:validate:fail 事件
// 触发 form:validate:after 事件
// 返回 true 或 false
}

var validateElement = function(element, validators) {
// 触发 element:validate:before 事件
// 根据 validators 中的函数名字,到 clientSideValidations.validators 中找到对应的函数并调用
// 触发 element:validate:pass 或 element:validate:fail 事件
// 触发 element:validate:after 事件
// 返回 true 或 false
}

// Main hook
// If new forms are dynamically introduced into the DOM the .validate() method
// must be invoked on that form
$(function() { $('form[data-validate]').validate(); })
})(jQuery);

基本就是这样了。
最后做个广告 [url=http://book.douban.com/subject/3590768/]《JavaScript语言精粹》[/url]、[url=http://book.douban.com/subject/10733304/]《基于MVC的JavaScript Web富应用开发》[/url],这两本书对于我这种写JS都是用哪儿查哪儿,没系统学过JS的人帮助很大。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值