Ajax是Asynchronous JavaScript and XML的缩写,是一种不需要重新整理页面,透过JavaScript来与服务器交换资料、更新网页内容的技术。目的在于改善使用者的操作接口,提升流畅度。它主要是透过浏览器提供的XMLHttpRequestObject
来达成,不过因为跨浏览器的困难度,大多数人们会选择使用JavaScript Library来处理Ajax,例如JQuery。虽然Ajax的缩写中包括XML,但是实务上并不一定要用XML格式,事实上也已经很少人使用XML当作传输的格式了。总归来说,依照Ajax使用的格式分类,有三种方式:
第一种方式非常简单,但是限制是一次只能更新一小块内容。Rails 默认使用第二种方式,程式撰写容易。而第三种方式则将 JavaScript 程式都放在客户端浏览器上,相较于第二种则多了解析 JSON 或 XML 的部份。以Web API的设计角度来看,与表现层无关的JSON格式是比较干净的,可以获得比较好的重复使用性。
讲解JavaScript和JQuery语法已经超过本书范围,本章接下来会假设读者已经有基本认识。身为一个Web程式设计师,不得不对JavaScript要有基本了解。
Rails 从 3.0 后将Ajax使用的JavaScript都改成Unobtrusive JavaScript(UJS)方式。什么是Unobtrusive呢?用个范例来说吧,它会将超连结改成用表单DELETE送出,并且用一个提示视窗来作确认:
link_to 'Remove', event_path(1), :method => :delete, :data => { :confirm => "Sure?" }
在Rails 3之前,会输出:
<a onclick="if (confirm('Sure?')) { var f = document.createElement('form'); f.style.display = 'none'; this.parentNode.appendChild(f); f.method = 'POST'; f.action = this.href;var m = document.createElement('input'); m.setAttribute('type', 'hidden'); m.setAttribute('name', '_method'); m.setAttribute('value', 'delete'); f.appendChild(m);f.submit(); };return false;" href="/events/1">Remove</a>
在Rails 3之后,会输出:
<a rel="nofollow" data-confirm="Sure?" data-method="delete" class="delete" href="/events/1">Remove</a>
Unobtrusive也就是将JavaScript程式与HTML完全分开,除了可以让HTML码干净之外,也可以支援更换不同的JavaScript Library,例如把Rails内建的jQuery换成Protytype.js或angular.js等等。
在Layout中有输出一段
<%= csrf_meta_tag %>
的作用就是搭配给UJS使用的,让JavaScript可以拿到CSRF安全验证码,我们会在安全一章讨论到什么是CSRF。
除了超连结 link_to 加上 :remote 可以变成 Ajax 之外,表单 form_for 也可以加上:remote
变成 Ajax。
form_for @user, :remote => true
同理于超连结 link_to,按钮 button_to 加上:remote => true
参数也会变成 Ajax。
除了已经看过的 :data => { :confirm => "Are you Sure?" }
之外,disable_with
可以避免使用者连续按下送出:
button_to "Remove", user_path(@user), :data => { :disable_with => 'Removing' }
编辑 app/views/events/index.html.erb 最下方加入:
<%= link_to 'Hello!', welcome_say_hello_path, :id => "ajax-load" %> <div id="content">
</div>
<script>
$(document).ready(function() {
$('#ajax-load').click( function(){
$('#content').load( $(this).attr("href") );
return false;
});
});
</script>
如此点下超连结后,就会把回传的HTML置入到<div id="content">
里面。
编辑 app/views/events/index.html.erb,在循环中间加入
<%= link_to 'ajax show', event_path(event), :remote => true %>
在循环外插入一个<div id="event_area"></div>
编辑 app/controllers/events_controller.rb,在 show action 中加入
respond_to do |format|
format.html
format.js
end
新增 app/views/events/_event.html.erb,内容与 show.html.erb 相同
新增 app/views/events/show.js.erb,内容如下
$('#event_area').html("<%= escape_javascript(render :partial => 'event') %>")
.css({ backgroundColor: '#ffff99' });
浏览 http://localhost:3000/events
escape_javascript()
可以缩写为j()
。
JavaScript Object Notation(JSON)是一种源自JavaScript的资料格式,是目前Web应用程式之间的准标准资料交换格式,在Rails之中,每个物件都有to_json
方法可以很方便的转换资料格式。
<%= link_to 'ajax show', event_path(event), :remote => true, :data => { :type => :json }, :class => "ajax_update" %>
点击ajax show就会送出Ajax request了,但接下来要怎么撰写处理JSON的程式呢?
<script>
$(document).ready(function() {
$('.ajax_update').on("ajax:success", function(event, data) {
var event_area = $('#event_area');
event_area.html( data.name );
});
});
</script>
当然,你也可以把HTML片段当做JSON的资料来传递。
另一种JSON的变形是JSONP(JSON with Padding),将JSON资料包在一个JavaScript function里,这个做的用处是让这个API可以跨网域被呼叫。要回传JSONP格式,只需要在render :json
时多一个参数是:callback
即可
respond_to do |format|
format.json { render :json => @user.to_json, :callback => "process_user" }
end
事实上,Rails默认让每个换页都用上了Ajax技巧,这一招叫做Turbolinks,在默认的Gemfile中可以看到gem "turbolinks"
,以及Layout中的data-turbolinks-track
。
它的作用是让每一个超连结都只用Ajax的方式将整个body
内容替换掉,这样换页时就不需要重新加载head
部份的标签,包括JavaScript和CSS等等,目的是可以改善换页时的速度。
要特别注意因为它没有整页重新加载,所以如果有放在application.js里面的$(document).ready
程式就变成只有第一次加载页面才执行到,换页时就失效了。这时候必须改成$(document).on("page:change", function(){ ...})
。
如果想要明显体会它的效果,可以在app/assets/javascripts/application.js
里面加上
Turbolinks.enableProgressBar();
最后,因为它会影响JavaScript的Event Bindings行为,所以在搭配一些JavaScript比较吃重的应用程式,例如使用JavaScript Framework如Backbone、AngularJS、Ember时会移除不用,以免互相影响。