Rails内建的Ajax/JavaScript功能详解
在 Rails 使用 JavaScript
本篇介绍 Rails 内建的 Ajax/JavaScript 功能。轻松打造丰富生动的 Ajax 应用程式。
读完本篇,您将了解:
Ajax 的基础。
如何将 JavaScript 与 HTML 分离(Unobtrusive JavaScript)。
如何使用 Rails 内建的帮助方法。
如何在服务器端处理 Ajax。
Turbolinks。
Ajax 介绍
要理解 Ajax,首先必须先了解浏览器平常的工作塬理。
在浏览器网址栏输入 http://localhost:3000,并按下 Enter。浏览器此时便向服务器发送请求。服务器接收请求,去拿所有需要的资源(assets),像是 JS、CSS、图片等,接着将这些资源,按照程式逻辑组合成网页,返回网页给浏览器。在网页裡按下某个连结,会重复刚刚的步骤:发送请求、抓取资源、组合页面、返回结果。这几个步骤通常称为“请求响应週期”(Request Response Cycle)。
JavaScript 也可向服务器发送请求,并解析响应。JavaScript 也具有更新网页的能力。熟悉 JavaScript 的开发者可以做到只更新部分的页面,而无需向服务器索要整个页面。这个强大的技术称为 Ajax。
Rails 出厂内建 CoffeeScript,故以下的例子皆以 CoffeeScript 撰写。当然这些例子也可用纯 JavaScript 写出来。
以下是用 CoffeeScript 使用 jQuery 发送 Ajax 请求的例子:
$.ajax(url: "/test").done (html) ->
$("#results").append html
这段程式从 /test 获取资料,并将资料附加在 id 为 #results 的 div 之后。
Rails 对于使用这种技巧来撰写网页,提供了相当多的官方支援。几乎很少会需要自己写这样的程式。以下章节将示範,如何用点简单的技术,便能用 Rails 写出应用了 Ajax 的网站。
Unobtrusive JavaScript
Rails 使用一种叫做 “Unobtrusive JavaScript” (缩写为 UJS)的技术来处理 DOM 操作。这是来自前端社群的最佳实践,但有些教学文件可能会用别种技术,来达成同样的事情。
以下是撰写 JavaScript 最简单的方式(行内 JavaScript):
<a href="#" onclick="this.style.backgroundColor='#990000'">Paint it red</a>
按下链接,背景就变红。如果按下连结后,要执行许多 JavaScript 程式码怎么办?
<a href="#" onclick="this.style.backgroundColor='#009900';this.style.color='#FFFFFF';">Paint it green</a>
尴尬吧?可以将 JavaScript 抽离出来,并用 CoffeeScript 改写:
paintIt = (element, backgroundColor, textColor) ->
element.style.backgroundColor = backgroundColor
if textColor?
element.style.color = textColor
接着换掉行内写法:
<a href="#" onclick="paintIt(this, '#990000')">Paint it red</a>
看起来好一点了,但多个连结都要有同样的效果呢?
<a href="#" onclick="paintIt(this, '#990000')">Paint it red</a>
<a href="#" onclick="paintIt(this, '#009900', '#FFFFFF')">Paint it green</a>
<a href="#" onclick="paintIt(this, '#000099', '#FFFFFF')">Paint it blue</a>
很不 DRY 啊。可以使用事件来简化。给每个连结加上 data-* 属性,接着给每个连结的 click 事件,加上一个处理函数:
paintIt = (element, backgroundColor, textColor) ->
element.style.backgroundColor = backgroundColor
if textColor?
element.style.color = textColor
$ ->
$("a[data-background-color]").click (e) ->
e.preventDefault()
backgroundColor = $(this).data("background-color")
textColor = $(this).data("text-color")
paintIt(this, backgroundColor, textColor)
<a href="#" data-background-color="#990000">Paint it red</a>
<a href="#" data-background-color="#009900" data-text-color="#FFFFFF">Paint it green</a>
<a href="#" data-background-color="#000099" data-text-color="#FFFFFF">Paint it blue</a>
这个技术称为 “Unobtrusive” JavaScript。因为 JavaScript 不再需要与 HTML 混在一起。之后便更容易修改,也更容易加新功能上去。任何连结只要加个 data- 属性,便可以得到同样效果。将 JavaScript 从 HTML 抽离后,JavaScript 便可透过合併压缩工具,让所有页面可以共用整份 JavaScript 。也就是说,只需在第一次戴入页面时下载一次,之后的页面使用快取的档案即可。Unobtrusive JavaScript 带来的好处非常多。
Rails 团队强烈建议採用这种风格来撰写 CoffeeScript (JavaScript),你会发现许多函式库也採用这种风格。
内建的 Ajax 帮助方法
Rails 在 View 提供了许多用 Ruby 写的帮助方法来产生 HTML。会想元素加上 Ajax?没问题,Rails 会帮助你。
Rails 的 “Ajax 帮助方法” 实际上分成用 JavaScript 所写的帮助方法,与用 Ruby 所写成的帮助方法。
用 JavaScript 写的部分可以在这找到 rails.js,而用 Ruby 写的部份就是 View 的帮助方法,用来给 DOM 新增适当的标籤。rails.js 裡的 CoffeeScript 会监听这些属性,执行相应的处理函数。
撰写表单的帮助方法。接受 :remote 选项:
<%= form_for(@article, remote: true) do |f| %>
...
<% end %>
产生的 HTML:
<form accept-charset="UTF-8" action="/articles" class="new_article" data-remote="true" id="new_article" method="post">
...
</form>
注意 data-remote="true"。有了这个属性之后,表单会透过 Ajax 提交,而不是浏览器平常的提交机制。
除了产生出来的<form> 之外,可能还想在提交成功与失败做某些处理。可以透过 ajax:success 与 ajax:error 事件,在提交成功与失败时,来附加内容至 DOM:
$(document).ready ->
$("#new_article").on("ajax:success", (e, data, status, xhr) ->
$("#new_article").append xhr.responseText
).on "ajax:error", (e, xhr, status, error) ->
$("#new_article").append " "<p>ERROR</p>"
当然这只是个开始,更多可用的事件可在 jQuery-ujs 的维基页面上可找到。
form_tag
跟 form_for 非常类似,接受 :remote 选项:
<%= form_tag('/articles', remote: true) %>
产生的 HTML:
<form accept-charset="UTF-8" action="/articles" data-remote="true" method="post">..
</form>link_to
产生连结的帮助方法。接受 :remote 选项:
<%= link_to "an article", @article, remote: true %>
产生的 HTML:
<a href="/artciles/1" data-remote="true">an article</a>
可以像上面 form_for 例子那样,绑定相同的 Ajax 事件上去。 来看个例子,假设按个按键,删除一篇文章,提示一些讯息。只需写一些 HTML:
<%= link_to "Delete artcile", @article, remote: true, method: :delete %>
再写一点 CoffeeScript:
$ ->
$("a[data-remote]").on "ajax:success", (e, data, status, xhr) ->
alert "The article was deleted."
就这么简单。
button_to
建立按钮的帮助方法。接受 :remote 选项:
<%= button_to "An article", @article, remote: true %>
会产生:
<form action="/articles/1" class="button_to" data-remote="true" method="post">
<div><input type="submit" value="An article">
<input name="authenticity_token" type="hidden" value="PVXViXMJCLd717CYN5Ty7/gTLF3iaqPhL33FTeBmoVk=">
</div>
</form>
由于这只是个<form>,所有 form_for 可用的东西,也可以应用在 button_to。
服务器端的考量
Ajax 不只是客户端的事,服务器也要出力。人们倾向 Ajax 请求回传 JSON,而不是 HTML,来看看如何回传 JSON。
简单的例子
假设有许多使用者,想给他们显示建立新帐号的表单。而 Controller 的 index 动作:
class UsersController < ApplicationController
def index
@users = User.all
@user = User.new
end # ...
以及 View (app/views/users/index.html.erb):
<b>Users</b>
<ul id="users">
<%= render @users %>
</ul>
<%= form_for(@user, remote: true) do |f| %>
<%= f.label :name %><br>
<%= f.text_field :name %>
<%= f.submit %>
<% end %>
app/views/users/_user.html.erb Partial:
<li><%= user.name %></li>
index 页面上半部列出用户,下半部提供新建用户的表单。
下面的表单会唿叫 Users Controller 的 create 动作。因为表单有 remote: true 这个选项,请求会使用 Ajax POST 到 UsersController,等待 Controller 回应 JavaScript。处理这个请求的 create 动作会像是:
# app/controllers/users_controller.rb
# ......def create
@user = User.new(params[:user])
respond_to do |format|
if @user.save
format.html { redirect_to @user, notice: 'User was successfully created.' }
format.js {}
format.json { render json: @user, status: :created, location: @user }
else
format.html { render action: "new" }
format.json { render json: @user.errors, status: :unprocessable_entity }
end
end
end
注意 respond_to 区块内的 format.js,这是 Cotroller 回应 Ajax 请求的地方。create 动作对应app/views/users/create.js.erb:
$("<%= escape_javascript(render @user) %>").appendTo("#users");
Turbolinks
Rails 4 出厂内建 Turbolinks RubyGem。Turbolinks 使用了 Ajax 技术,可以加速页面的渲染。
Turbolinks 工作塬理
Turbolinks 给页面上所有的 a 标籤添加了一个 click 处理函数。如果浏览器支援 PushState,Turbolinks 会对页面发出 Ajax 请求,解析服务器回过来的响应,把页面整个 用响应回传的 换掉。接着 Turbolinks 会利用 PushState 把 URL 换成正确的,看起来就像重新整理一样,仍保有漂亮的 URL。
启用 Turbolinks 只需在 Gemfile 加入:
gem 'turbolinks'
并在 CoffeeScript Manifest 档案(app/assets/javascripts/application.js)裡加入:
//= require turbolinks
若有些连结要禁用 Turbolinks,给该连结加上 data-no-turbolink 属性即可:
<a href="..." data-no-turbolink>No turbolinks here</a>.
页面变化的事件
撰写 CoffeeScript 时,通常会想在页面加载时做些处理,搭配 jQuery,通常会写出像是下面的程式码:
$(document).ready ->
alert "page has loaded!"
而 Turbolinks 覆写了页面加载逻辑,依赖 $(document).ready 事件的程式码不会被触发。若是写了类似上例的程式码,必须改写成:
$(document).on "page:change", ->
alert "page has loaded!"
- 评论列表(网友评论仅供网友表达个人看法,并不表明本站同意其观点或证实其描述)
-
