Rails 3.1預設的JavaScript程式庫是jQuery,在 版型(Layout) 中看過,預設會包括jquery.js,而一些頁面的「神奇魔法」,則是由jquery_ujs.js來處理與銜接jQuery,例如 版型(Layout)中介紹過,以link_to輔助方法為例,在按下其產生的鏈結時,會執行jquery_ujs.js中的程式碼,動態建立表單並使用表單物件的submit發送:
// Handles "data-method" on links such as:
// <a href="/users/5" data-method="delete" rel="nofollow" data-confirm="Are you sure?">Delete</a>
handleMethod: function(link) {
var href = link.attr('href'),
method = link.data('method'),
target = link.attr('target'),
csrf_token = \$('meta[name=csrf-token]').attr('content'),
csrf_param = \$('meta[name=csrf-param]').attr('content'),
form = \$('<form method="post" action="' + href + '"></form>'),
metadata_input = '<input name="_method" value="' + method + '" type="hidden" />';
if (csrf_param !== undefined && csrf_token !== undefined) {
metadata_input += '<input name="' + csrf_param + '" value="' + csrf_token + '" type="hidden" />';
}
if (target) { form.attr('target', target); }
form.hide().append(metadata_input).appendTo('body');
form.submit();
},
handleMethod是以submit發送表單,也就是所謂同步表單或非Ajax表單,有些輔助方法可以設定:remote => true,此時就會以非同步方式來發送,也就是所謂Ajax方式,以link_to輔助方法為例,若加上:remote => true,則產生的超鏈結會加上data-remote="true"(form_for、button_to等也有:remote可以設定)。例如:
<a href="/messages/1" data-remote="true">Show</a>
標記為data-remote="true"的鏈結,會使用handleRemote方法處理,簡單來說,就是以非同步物件發送:
// Submits "remote" forms and links with ajax
handleRemote: function(element) {
var method, url, data,
crossDomain = element.data('cross-domain') || null,
dataType = element.data('type') || (\$.ajaxSettings && \$.ajaxSettings.dataType),
options;
...
options = {
....略
};
// Only pass url to `ajax` options if not blank
if (url) { options.url = url; }
return rails.ajax(options);
} else {
return false;
}
},
按下鏈結之後,由於是非同步回應,所以不會換頁,你要根據回應自行處理畫面,預設收到的回應是JavaScript,也可以指定JSON等其它格式。
舉例來說,可修改 觀摩 Scaffold 中的index.html.erb如下:
- index.html.erb
<h1>Listing messages</h1>
<div id="message"></div>
<table>
<tr>
<th>Name</th>
<th>Title</th>
<th></th>
<th></th>
<th></th>
</tr>
<% @messages.each do |message| %>
<tr>
<td><%= message.name %></td>
<td><%= message.title %></td>
<td><%= link_to 'Show', message, :remote => true %></td>
<td><%= link_to 'Edit', edit_message_path(message) %></td>
<td><%= link_to 'Destroy', message, confirm: 'Are you sure?', method: :delete %></td>
</tr>
<% end %>
</table>
<br />
<%= link_to 'New Message', new_message_path %>
如此按下Show鏈結就不會換頁,而會以非同步方式發送請求,接著可以修改messages_controller.rb:
- messages_controller.rb
class MessagesController < ApplicationController
...
def show
@message = Message.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.js # show.js.erb
format.json { render json: @message }
end
end
...
end
非同步請求預設希望取得JavaScript回應,否則取得HTML回應,如上設定之後,若有個show.js.erb:
- show.js.erb
\$('#message').html("<p><b>Content:</b><%= @message.content %></p>")
.css({ backgroundColor: '#ffff99' });
接下來如果按下Show鏈結,就會在同頁面中<div id="message"></div>載入訊息內容。
如果想取得的回應是JSON格式,例如:
- index.html.erb
<h1>Listing messages</h1>
<div id="message"></div>
<table>
<tr>
<th>Name</th>
<th>Title</th>
<th></th>
<th></th>
<th></th>
</tr>
<% @messages.each do |message| %>
<tr>
<td><%= message.name %></td>
<td><%= message.title %></td>
<td><%= link_to 'Show', message_path(:id => message, :format => :json), :remote => true %></td>
<td><%= link_to 'Edit', edit_message_path(message) %></td>
<td><%= link_to 'Destroy', message, confirm: 'Are you sure?', method: :delete %></td>
</tr>
<% end %>
</table>
<br />
<%= link_to 'New Message', new_message_path %>
<script>
\$(function() {
\$('a[data-remote]').bind("ajax:success", function(event, data) {
\$('#message').html('<p><b>Content:</b>' + data.content + '</p>')
.css({ backgroundColor: '#ffff99' });
});
});
</script>
按下Show鏈結,就會在同頁面中<div id="message"></div>載入訊息內容。
Rails 3中基本上對Ajax沒有提供太多神奇的魔法,DHH建議自己要親自撰寫JavaScript來處理一些事,這意謂著你對Ajax必須有更多的瞭解,你要嘛將JavaScript放在伺服端,結合Erb之類的來輔助產生內容,要嘛將JavaScript放在客戶端,並與伺服端協定好資料格式,或必要時撰寫一些頁面輔助方法來簡化頁面與伺服端的溝通。
如果要提供JSONP,只要在render時指定:json與:callback即可。例如:
- messages_controller.rb
class MessagesController < ApplicationController
...
def show
@message = Message.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.js # show.js.erb
format.json { render json: @message, :callback => params[:callback] }
end
end
...
end
如果使用者指定請求參數callback=process_message,則一個回應範例如下: