UJS是Rails 3引入的JavaScript框架与Rails的抽象层。我们知道Rails一些Helper是依赖于JavaScript框架的,比如Ajax Form,Ajax Link等,并且在Rails 3之前默认集成的JavaScript框架是Prototype,再这之后才换成了社区呼声很高的jQuery。
如前面所说UJS是个抽象层,它需要在每个框架上实现对应的接口,比如官方实现了jquery-ujs和prototype-ujs。本篇主要以jquery-ujs为例来讲解UJS。jquery-ujs代码很短,只有500行不到,想先浏览一下整个代码可访问Github的jquery-ujs repo。
data-confirm
先从最简单confirm例子入手。比如,在某些链接上让用户在点击链接后再次确认一次,我们一般会这么写
1
|
|
这里和普通的链接不同的地方只是在于多加了一个data-confirm
属性,然后UJS帮你实现了弹出确认框。这其中的实现方法很简单,写过jQuery的同学都会,就是监听所有链接的click
事件,当这个被点击链接上有data-confirm
属性时,取出data-confirm
中的文本,弹出确认框,并根据用户的操作选择是否中断这个点击事件的处理。
data-method
然后我们来看看,另外一种比较常见的链接用法,让链接点击时使用非GET方法请求对应的URL,代码如下
1
|
|
这里传入的method
参数,生成HTML时会被转换为data-method="delete"
。与前面一个例子一样,UJS在这个链接的click事件上监听,当这个链接有data-method
属性时,创建一个隐藏的form
标签,并附带上名为_method
参数,值为data-method
属性值的input
标签,最后将这个构造的表单提交。
通过这样的小技巧,Rails开发者就能通过<a>
标签以任何想要的HTTP Method请求对应的链接。
Ajax Form
在Rails 3之后的form_tag
和form_for
上传入remote: true
就能实现表单的Ajax提交,同样这个事情,UJS也是通过监听所有的Form标签的submit
事件,然后检测标签上的data-remote
属性来实现的。
对于开发者,在传入了remote: true
之后要怎样去插入对应的Ajax处理器呢?UJS在对应Ajax提交阶段上触发了Rails自定义的ajax:xxx
事件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
所以基于UJS我们可以这样直接在form
元素上绑定上对应的Ajax提交处理代码
1 2 3 4 5 |
|
UJS在Ajax表单提交了之后,还会将该表单中的button
和input[type='submit']
都加上disable
属性,防止用户多次点击引发多次提交。
此外,UJS实现的Ajax Form上还有两个特殊的事件
ajax:aborted:required
当表单提交的时候,有未填的input
标签时会触发这个事件,你可以选择去处理ajax:aborted:file
我们知道通过正常的方式是无法通过AJAX来提交文件的,当表单里包含了文件字段的时候,这个事件会被触发,在这里可以去实现自己的文件提交逻辑。比如remotipart通过这个事件实现了Ajax Form的文件提交。
选择器
UJS的所有功能都通过预设的选择器,绑定事件处理逻辑到对应元素上,以下是选择器的定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
这提供可配置对应选择器的机会,比方说使用某个jQuery插件,它通过data-remote
去标记别的事情。那么在不修改这个插件的情况下想让UJS继续工作,我们可以重新配置UJS的选择器
1
|
|
CSRF Token
UJS还会在每次表单提交上自动附带上CSRF Token。Rails 3之后强制所有的非幂等HTTP请求需要带上CSRF token作安全校验,用来防止XSS攻击。这就要求开发者在每次写Ajax请求的时候,都需要手动把这部分的token带上,UJS也通过jQuery的ajaxPrefilter接口,让每次的Ajax请求都自动附带上CSRF token。
另外,每次UJS初始化时,会为页面上所有表单都加上带有CSRF Token的隐藏input
标签,让表单在提交时都能自动带上CSRF Token。
从UJS学到了什么
- jquery-ujs的所有事件绑定都是绑定在document,等到事件触发后再分发到对应的事件处理逻辑里,不需要在初始化时查找对应的元素并绑定事件
- 充分利用HTML5的
data
属性来解耦事件处理逻辑,将各种参数序列化到data
属性 - 利用jQuery的自定义事件更好地定制自己的事件处理流程
参考
- http://www.alfajango.com/blog/rails-3-remote-links-and-forms/
- http://www.alfajango.com/blog/rails-3-remote-links-and-forms-data-type-with-jquery/
- http://railscasts.com/episodes/205-unobtrusive-javascript
- http://net.tutsplus.com/tutorials/javascript-ajax/using-unobtrusive-javascript-and-ajax-with-rails-3/