-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathworking_with_javascript_in_rails.html
517 lines (464 loc) · 30.8 KB
/
working_with_javascript_in_rails.html
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="zh-CN" lang="zh-CN">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>在 Rails 中使用 JavaScript — Ruby on Rails 指南</title>
<link rel="stylesheet" type="text/css" href="stylesheets/style.css" />
<link rel="stylesheet" type="text/css" href="stylesheets/print.css" media="print" />
<link rel="stylesheet" type="text/css" href="stylesheets/syntaxhighlighter/shCore.css" />
<link rel="stylesheet" type="text/css" href="stylesheets/syntaxhighlighter/shThemeRailsGuides.css" />
<link rel="stylesheet" type="text/css" href="stylesheets/fixes.css" />
<link href="images/favicon.ico" rel="shortcut icon" type="image/x-icon" />
</head>
<body class="guide">
<div id="topNav">
<div class="wrapper">
<strong class="more-info-label">更多内容 <a href="http://rubyonrails.org/">rubyonrails.org:</a> </strong>
<span class="red-button more-info-button">
更多内容
</span>
<ul class="more-info-links s-hidden">
<li class="more-info"><a href="http://rubyonrails.org/">综览</a></li>
<li class="more-info"><a href="http://rubyonrails.org/download">下载</a></li>
<li class="more-info"><a href="http://rubyonrails.org/deploy">部署</a></li>
<li class="more-info"><a href="https://github.com/rails/rails">源码</a></li>
<li class="more-info"><a href="http://rubyonrails.org/screencasts">视频</a></li>
<li class="more-info"><a href="http://rubyonrails.org/documentation">文件</a></li>
<li class="more-info"><a href="http://rubyonrails.org/community">社群</a></li>
<li class="more-info"><a href="http://weblog.rubyonrails.org/">Blog</a></li>
</ul>
</div>
</div>
<div id="header">
<div class="wrapper clearfix">
<h1><a href="index.html" title="回首页">Guides.rubyonrails.org</a></h1>
<ul class="nav">
<li><a class="nav-item" href="index.html">首页</a></li>
<li class="guides-index guides-index-large">
<a href="index.html" id="guidesMenu" class="guides-index-item nav-item">指南目录</a>
<div id="guides" class="clearfix" style="display: none;">
<hr />
<dl class="L">
<dt>入门</dt>
<dd><a href="getting_started.html">Rails 入门</a></dd>
<dt>模型</dt>
<dd><a href="active_record_basics.html">Active Record 基础</a></dd>
<dd><a href="active_record_migrations.html">Active Record 数据库迁移</a></dd>
<dd><a href="active_record_validations.html">Active Record 数据验证</a></dd>
<dd><a href="active_record_callbacks.html">Active Record 回调</a></dd>
<dd><a href="association_basics.html">Active Record 关联</a></dd>
<dd><a href="active_record_querying.html">Active Record 查询</a></dd>
<dt>视图</dt>
<dd><a href="layouts_and_rendering.html">Rails 布局和视图渲染</a></dd>
<dd><a href="form_helpers.html">Action View 表单帮助方法</a></dd>
<dt>控制器</dt>
<dd><a href="action_controller_overview.html">Action Controller 简介</a></dd>
<dd><a href="routing.html">Rails 路由全解</a></dd>
</dl>
<dl class="R">
<dt>深入</dt>
<dd><a href="active_support_core_extensions.html">Active Support 核心扩展</a></dd>
<dd><a href="i18n.html">Rails 国际化 API</a></dd>
<dd><a href="action_mailer_basics.html">Action Mailer 基础</a></dd>
<dd><a href="active_job_basics.html">Active Job 基础</a></dd>
<dd><a href="security.html">Rails 安全指南</a></dd>
<dd><a href="debugging_rails_applications.html">调试 Rails 程序</a></dd>
<dd><a href="configuring.html">设置 Rails 程序</a></dd>
<dd><a href="command_line.html">Rails 命令行</a></dd>
<dd><a href="asset_pipeline.html">Asset Pipeline</a></dd>
<dd><a href="working_with_javascript_in_rails.html">在 Rails 中使用 JavaScript</a></dd>
<dd><a href="constant_autoloading_and_reloading.html">Constant Autoloading and Reloading</a></dd>
<dt>扩展 Rails</dt>
<dd><a href="rails_on_rack.html">Rails on Rack</a></dd>
<dd><a href="generators.html">客制与新建 Rails 产生器</a></dd>
<dd><a href="rails_application_templates.html">Rails 应用程式模版</a></dd>
<dt>贡献 Ruby on Rails</dt>
<dd><a href="contributing_to_ruby_on_rails.html">贡献 Ruby on Rails</a></dd>
<dd><a href="api_documentation_guidelines.html">API 文件准则</a></dd>
<dd><a href="ruby_on_rails_guides_guidelines.html">Ruby on Rails 指南准则</a></dd>
<dt>维护方针</dt>
<dd><a href="maintenance_policy.html">维护方针</a></dd>
<dt>发布记</dt>
<dd><a href="upgrading_ruby_on_rails.html">升级 Ruby on Rails</a></dd>
<dd><a href="4_2_release_notes.html">Ruby on Rails 4.2 发布记</a></dd>
<dd><a href="4_1_release_notes.html">Ruby on Rails 4.1 发布记</a></dd>
<dd><a href="4_0_release_notes.html">Ruby on Rails 4.0 发布记</a></dd>
<dd><a href="3_2_release_notes.html">Ruby on Rails 3.2 发布记</a></dd>
<dd><a href="3_1_release_notes.html">Ruby on Rails 3.1 发布记</a></dd>
<dd><a href="3_0_release_notes.html">Ruby on Rails 3.0 发布记</a></dd>
<dd><a href="2_3_release_notes.html">Ruby on Rails 2.3 发布记</a></dd>
<dd><a href="2_2_release_notes.html">Ruby on Rails 2.2 发布记</a></dd>
</dl>
</div>
</li>
<!-- <li><a class="nav-item" href="//github.com/docrails-tw/wiki">参与翻译</a></li> -->
<li><a class="nav-item" href="https://github.com/ruby-china/guides/blob/master/CONTRIBUTING.md">贡献</a></li>
<li><a class="nav-item" href="credits.html">致谢</a></li>
<li class="guides-index guides-index-small">
<select class="guides-index-item nav-item">
<option value="index.html">指南目录</option>
<optgroup label="入门">
<option value="getting_started.html">Rails 入门</option>
</optgroup>
<optgroup label="模型">
<option value="active_record_basics.html">Active Record 基础</option>
<option value="active_record_migrations.html">Active Record 数据库迁移</option>
<option value="active_record_validations.html">Active Record 数据验证</option>
<option value="active_record_callbacks.html">Active Record 回调</option>
<option value="association_basics.html">Active Record 关联</option>
<option value="active_record_querying.html">Active Record 查询</option>
</optgroup>
<optgroup label="视图">
<option value="layouts_and_rendering.html">Rails 布局和视图渲染</option>
<option value="form_helpers.html">Action View 表单帮助方法</option>
</optgroup>
<optgroup label="控制器">
<option value="action_controller_overview.html">Action Controller 简介</option>
<option value="routing.html">Rails 路由全解</option>
</optgroup>
<optgroup label="深入">
<option value="active_support_core_extensions.html">Active Support 核心扩展</option>
<option value="i18n.html">Rails 国际化 API</option>
<option value="action_mailer_basics.html">Action Mailer 基础</option>
<option value="active_job_basics.html">Active Job 基础</option>
<option value="security.html">Rails 安全指南</option>
<option value="debugging_rails_applications.html">调试 Rails 程序</option>
<option value="configuring.html">设置 Rails 程序</option>
<option value="command_line.html">Rails 命令行</option>
<option value="asset_pipeline.html">Asset Pipeline</option>
<option value="working_with_javascript_in_rails.html">在 Rails 中使用 JavaScript</option>
<option value="constant_autoloading_and_reloading.html">Constant Autoloading and Reloading</option>
</optgroup>
<optgroup label="扩展 Rails">
<option value="rails_on_rack.html">Rails on Rack</option>
<option value="generators.html">客制与新建 Rails 产生器</option>
<option value="rails_application_templates.html">Rails 应用程式模版</option>
</optgroup>
<optgroup label="贡献 Ruby on Rails">
<option value="contributing_to_ruby_on_rails.html">贡献 Ruby on Rails</option>
<option value="api_documentation_guidelines.html">API 文件准则</option>
<option value="ruby_on_rails_guides_guidelines.html">Ruby on Rails 指南准则</option>
</optgroup>
<optgroup label="维护方针">
<option value="maintenance_policy.html">维护方针</option>
</optgroup>
<optgroup label="发布记">
<option value="upgrading_ruby_on_rails.html">升级 Ruby on Rails</option>
<option value="4_2_release_notes.html">Ruby on Rails 4.2 发布记</option>
<option value="4_1_release_notes.html">Ruby on Rails 4.1 发布记</option>
<option value="4_0_release_notes.html">Ruby on Rails 4.0 发布记</option>
<option value="3_2_release_notes.html">Ruby on Rails 3.2 发布记</option>
<option value="3_1_release_notes.html">Ruby on Rails 3.1 发布记</option>
<option value="3_0_release_notes.html">Ruby on Rails 3.0 发布记</option>
<option value="2_3_release_notes.html">Ruby on Rails 2.3 发布记</option>
<option value="2_2_release_notes.html">Ruby on Rails 2.2 发布记</option>
</optgroup>
</select>
</li>
</ul>
</div>
</div>
</div>
<hr class="hide" />
<div id="feature">
<div class="wrapper">
<h2>在 Rails 中使用 JavaScript</h2><p>本文介绍 Rails 内建对 Ajax 和 JavaScript 等的支持,使用这些功能可以轻易的开发强大的 Ajax 程序。</p><p>读完本文,你将学到:</p>
<ul>
<li>Ajax 基本知识;</li>
<li>非侵入式 JavaScript;</li>
<li>如何使用 Rails 内建的帮助方法;</li>
<li>如何在服务器端处理 Ajax;</li>
<li>Turbolinks 简介;</li>
</ul>
<div id="subCol">
<h3 class="chapter"><img src="images/chapters_icon.gif" alt="" />Chapters</h3>
<ol class="chapters">
<li><a href="#ajax-%E7%AE%80%E4%BB%8B">Ajax 简介</a></li>
<li><a href="#%E9%9D%9E%E4%BE%B5%E5%85%A5%E5%BC%8F-javascript">非侵入式 JavaScript</a></li>
<li>
<a href="#%E5%86%85%E5%BB%BA%E7%9A%84%E5%B8%AE%E5%8A%A9%E6%96%B9%E6%B3%95">内建的帮助方法</a>
<ul>
<li><a href="#form_for"><code>form_for</code></a></li>
<li><a href="#form_tag"><code>form_tag</code></a></li>
<li><a href="#link_to"><code>link_to</code></a></li>
<li><a href="#button_to"><code>button_to</code></a></li>
</ul>
</li>
<li>
<a href="#%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%AB%AF%E5%A4%84%E7%90%86">服务器端处理</a>
<ul>
<li><a href="#%E4%B8%80%E4%B8%AA%E7%AE%80%E5%8D%95%E7%9A%84%E4%BE%8B%E5%AD%90">一个简单的例子</a></li>
</ul>
</li>
<li>
<a href="#turbolinks">Turbolinks</a>
<ul>
<li><a href="#turbolinks-%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86">Turbolinks 的工作原理</a></li>
<li><a href="#%E9%A1%B5%E9%9D%A2%E5%86%85%E5%AE%B9%E5%8F%98%E6%9B%B4%E4%BA%8B%E4%BB%B6">页面内容变更事件</a></li>
</ul>
</li>
<li><a href="#%E5%85%B6%E4%BB%96%E8%B5%84%E6%BA%90">其他资源</a></li>
</ol>
</div>
</div>
</div>
<div id="container">
<div class="wrapper">
<div id="mainCol">
<h3 id="ajax-简介">1 Ajax 简介</h3><p>在理解 Ajax 之前,要先知道网页浏览器常规的工作原理。</p><p>在浏览器的地址栏中输入 <code>http://localhost:3000</code> 后,浏览器(客户端)会向服务器发起一个请求。然后浏览器会处理响应,获取相关的资源文件,比如 JavaScript、样式表、图片,然后显示页面内容。点击链接后发生的事情也是如此:获取页面内容,获取资源文件,把全部内容放在一起,显示最终的网页。这个过程叫做“请求-响应循环”。</p><p>JavaScript 也可以向服务器发起请求,并处理响应。而且还能更新网页中的内容。因此,JavaScript 程序员可以编写只需更新部分内容的网页,而不用从服务器获取完整的页面数据。这是一种强大的技术,我们称之为 Ajax。</p><p>Rails 默认支持 CoffeeScript,后文所有的示例都用 CoffeeScript 编写。本文介绍的技术,在普通的 JavaScript 中也可使用。</p><p>例如,下面这段 CoffeeScript 代码使用 jQuery 发起一个 Ajax 请求:</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$.ajax(url: "/test").done (html) ->
$("#results").append html
</pre>
</div>
<p>这段代码从 <code>/test</code> 地址上获取数据,然后把结果附加到 <code>div#results</code>。</p><p>Rails 内建了很多使用这种技术开发程序的功能,基本上无需自己动手编写上述代码。后文介绍 Rails 如何为开发这种程序提供帮助,不过都构建在这种简单的技术之上。</p><h3 id="非侵入式-javascript">2 非侵入式 JavaScript</h3><p>Rails 使用一种叫做“非侵入式 JavaScript”(Unobtrusive JavaScript)的技术把 JavaScript 应用到 DOM 上。非侵入式 JavaScript 是前端开发社区推荐使用的方法,但有些教程可能会使用其他方式。</p><p>下面是编写 JavaScript 最简单的方式,你可能见过,这叫做“行间 JavaScript”:</p><div class="code_container">
<pre class="brush: xml; gutter: false; toolbar: false">
<a href="#" onclick="this.style.backgroundColor='#990000'">Paint it red</a>
</pre>
</div>
<p>点击链接后,链接的背景会变成红色。这种用法的问题是,如果点击链接后想执行大量代码怎么办?</p><div class="code_container">
<pre class="brush: xml; gutter: false; toolbar: false">
<a href="#" onclick="this.style.backgroundColor='#009900';this.style.color='#FFFFFF';">Paint it green</a>
</pre>
</div>
<p>太别扭了,不是吗?我们可以把处理点击的代码定义成一个函数,用 CoffeeScript 编写如下:</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
paintIt = (element, backgroundColor, textColor) ->
element.style.backgroundColor = backgroundColor
if textColor?
element.style.color = textColor
</pre>
</div>
<p>然后在页面中这么做:</p><div class="code_container">
<pre class="brush: xml; gutter: false; toolbar: false">
<a href="#" onclick="paintIt(this, '#990000')">Paint it red</a>
</pre>
</div>
<p>这种方法好点儿,但是如果很多链接需要同样的效果该怎么办呢?</p><div class="code_container">
<pre class="brush: xml; gutter: false; toolbar: false">
<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>
</pre>
</div>
<p>非常不符合 DRY 原则。为了解决这个问题,我们可以使用“事件”。在链接上添加一个 <code>data-*</code> 属性,然后把处理程序绑定到拥有这个属性的点击事件上:</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
paintIt = (element, backgroundColor, textColor) ->
element.style.backgroundColor = backgroundColor
if textColor?
element.style.color = textColor
$ ->
$("a[data-background-color]").click ->
backgroundColor = $(this).data("background-color")
textColor = $(this).data("text-color")
paintIt(this, backgroundColor, textColor)
</pre>
</div>
<div class="code_container">
<pre class="brush: xml; gutter: false; toolbar: false">
<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>
</pre>
</div>
<p>我们把这种方法称为“非侵入式 JavaScript”,因为 JavaScript 代码不再和 HTML 混用。我们把两中代码完全分开,这么做易于修改功能。我们可以轻易地把这种效果应用到其他链接上,只要添加相应的 <code>data</code> 属性就行。所有 JavaScript 代码都可以放在一个文件中,进行压缩,每个页面都使用这个 JavaScript 文件,因此只在第一次请求时加载,后续请求会直接从缓存中读取。“非侵入式 JavaScript”带来的好处太多了。</p><p>Rails 团队极力推荐使用这种方式编写 CoffeeScript 和 JavaScript,而且你会发现很多代码库都沿用了这种方式。</p><h3 id="内建的帮助方法">3 内建的帮助方法</h3><p>Rails 提供了很多视图帮助方法协助你生成 HTML,如果想在元素上实现 Ajax 效果也没问题。</p><p>因为使用的是非侵入式 JavaScript,所以 Ajax 相关的帮助方法其实分成两部分,一部分是 JavaScript 代码,一部分是 Ruby 代码。</p><p><a href="https://github.com/rails/jquery-ujs/blob/master/src/rails.js">rails.js</a> 提供 JavaScript 代码,常规的 Ruby 视图帮助方法用来生成 DOM 标签。rails.js 中的 CoffeeScript 会监听这些属性,执行相应的处理程序。</p><h4 id="form_for">3.1 <code>form_for</code>
</h4><p><a href="http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_for"><code>form_for</code></a> 方法协助编写表单,可指定 <code>:remote</code> 选项,用法如下:</p><div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false">
<%= form_for(@post, remote: true) do |f| %>
...
<% end %>
</pre>
</div>
<p>生成的 HTML 如下:</p><div class="code_container">
<pre class="brush: xml; gutter: false; toolbar: false">
<form accept-charset="UTF-8" action="/posts" class="new_post" data-remote="true" id="new_post" method="post">
...
</form>
</pre>
</div>
<p>注意 <code>data-remote="true"</code> 属性,现在这个表单不会通过常规的提交按钮方式提交,而是通过 Ajax 提交。</p><p>或许你并不需要一个只能填写内容的表单,而是想在表单提交成功后做些事情。为此,我们要绑定到 <code>ajax:success</code> 事件上。处理表单提交失败的程序要绑定到 <code>ajax:error</code> 事件上。例如:</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$(document).ready ->
$("#new_post").on("ajax:success", (e, data, status, xhr) ->
$("#new_post").append xhr.responseText
).on "ajax:error", (e, xhr, status, error) ->
$("#new_post").append "<p>ERROR</p>"
</pre>
</div>
<p>显然你需要的功能比这要复杂,上面的例子只是个入门。关于事件的更多内容请阅读 <a href="https://github.com/rails/jquery-ujs/wiki/ajax">jquery-ujs 的维基</a>。</p><h4 id="form_tag">3.2 <code>form_tag</code>
</h4><p><a href="http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html#method-i-form_tag"><code>form_tag</code></a> 方法的功能和 <code>form_for</code> 类似,也可指定 <code>:remote</code> 选项,如下所示:</p><div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false">
<%= form_tag('/posts', remote: true) do %>
...
<% end %>
</pre>
</div>
<p>生成的 HTML 如下:</p><div class="code_container">
<pre class="brush: xml; gutter: false; toolbar: false">
<form accept-charset="UTF-8" action="/posts" data-remote="true" method="post">
...
</form>
</pre>
</div>
<p>其他用法都和 <code>form_for</code> 一样。详细介绍参见文档。</p><h4 id="link_to">3.3 <code>link_to</code>
</h4><p><a href="http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to"><code>link_to</code></a> 方法用来生成链接,可以指定 <code>:remote</code>,用法如下:</p><div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false">
<%= link_to "a post", @post, remote: true %>
</pre>
</div>
<p>生成的 HTML 如下:</p><div class="code_container">
<pre class="brush: xml; gutter: false; toolbar: false">
<a href="/posts/1" data-remote="true">a post</a>
</pre>
</div>
<p>绑定的 Ajax 事件和 <code>form_for</code> 方法一样。下面举个例子。加入有一个文章列表,我们想只点击一个链接就删除所有文章,视图代码如下:</p><div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false">
<%= link_to "Delete post", @post, remote: true, method: :delete %>
</pre>
</div>
<p>CoffeeScript 代码如下:</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ ->
$("a[data-remote]").on "ajax:success", (e, data, status, xhr) ->
alert "The post was deleted."
</pre>
</div>
<h4 id="button_to">3.4 <code>button_to</code>
</h4><p><a href="http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-button_to"><code>button_to</code></a> 方法用来生成按钮,可以指定 <code>:remote</code> 选项,用法如下:</p><div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false">
<%= button_to "A post", @post, remote: true %>
</pre>
</div>
<p>生成的 HTML 如下:</p><div class="code_container">
<pre class="brush: xml; gutter: false; toolbar: false">
<form action="/posts/1" class="button_to" data-remote="true" method="post">
<div><input type="submit" value="A post"></div>
</form>
</pre>
</div>
<p>因为生成的就是一个表单,所以 <code>form_for</code> 的全部信息都适用于这里。</p><h3 id="服务器端处理">4 服务器端处理</h3><p>Ajax 不仅需要编写客户端代码,服务器端也要做处理。Ajax 请求一般不返回 HTML,而是 JSON。下面详细介绍处理过程。</p><h4 id="一个简单的例子">4.1 一个简单的例子</h4><p>假设在网页中要显示一系列用户,还有一个新建用户的表单,控制器的 <code>index</code> 动作如下所示:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
class UsersController < ApplicationController
def index
@users = User.all
@user = User.new
end
# ...
</pre>
</div>
<p><code>index</code> 动作的视图(<code>app/views/users/index.html.erb</code>)如下:</p><div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false">
<b>Users</b>
<ul id="users">
<%= render @users %>
</ul>
<br>
<%= form_for(@user, remote: true) do |f| %>
<%= f.label :name %><br>
<%= f.text_field :name %>
<%= f.submit %>
<% end %>
</pre>
</div>
<p><code>app/views/users/_user.html.erb</code> 局部视图如下:</p><div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false">
<li><%= user.name %></li>
</pre>
</div>
<p><code>index</code> 动作的上部显示用户,下部显示新建用户的表单。</p><p>下部的表单会调用 <code>UsersController</code> 的 <code>create</code> 动作。因为表单的 <code>remote</code> 属性为 <code>true</code>,所以发往 <code>UsersController</code> 的是 Ajax 请求,使用 JavaScript 处理。要想处理这个请求,控制器的 <code>create</code> 动作应该这么写:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
# 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
</pre>
</div>
<p>注意,在 <code>respond_to</code> 的代码块中使用了 <code>format.js</code>,这样控制器才能处理 Ajax 请求。然后还要新建 <code>app/views/users/create.js.erb</code> 视图文件,编写发送响应以及在客户端执行的 JavaScript 代码。</p><div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false">
$("<%= escape_javascript(render @user) %>").appendTo("#users");
</pre>
</div>
<h3 id="turbolinks">5 Turbolinks</h3><p>Rails 4 提供了 <a href="https://github.com/rails/turbolinks">Turbolinks gem</a>,这个 gem 可用于大多数程序,加速页面渲染。</p><h4 id="turbolinks-的工作原理">5.1 Turbolinks 的工作原理</h4><p>Turbolinks 为页面中所有的 <code><a></code> 元素添加了一个点击事件处理程序。如果浏览器支持 <a href="http://dwz.cn/pushstate">PushState</a>,Turbolinks 会发起 Ajax 请求,处理响应,然后使用响应主体替换原始页面的整个 <code><body></code> 元素。最后,使用 PushState 技术更改页面的 URL,让新页面可刷新,并且有个精美的 URL。</p><p>要想使用 Turbolinks,只需将其加入 <code>Gemfile</code>,然后在 <code>app/assets/javascripts/application.js</code> 中加入 <code>//= require turbolinks</code> 即可。</p><p>如果某个链接不想使用 Turbolinks,可以在链接中添加 <code>data-no-turbolink</code> 属性:</p><div class="code_container">
<pre class="brush: xml; gutter: false; toolbar: false">
<a href="..." data-no-turbolink>No turbolinks here</a>.
</pre>
</div>
<h4 id="页面内容变更事件">5.2 页面内容变更事件</h4><p>编写 CoffeeScript 代码时,经常需要在页面加载时做一些事情。在 jQuery 中,我们可以这么写:</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$(document).ready ->
alert "page has loaded!"
</pre>
</div>
<p>不过,因为 Turbolinks 改变了常规的页面加载流程,所以不会触发这个事件。如果编写了类似上面的代码,要将其修改为:</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$(document).on "page:change", ->
alert "page has loaded!"
</pre>
</div>
<p>其他可用事件等详细信息,请参阅 <a href="https://github.com/rails/turbolinks/blob/master/README.md">Turbolinks 的说明文件</a>。</p><h3 id="其他资源">6 其他资源</h3><p>下面列出一些链接,可以帮助你进一步学习:</p>
<ul>
<li><a href="https://github.com/rails/jquery-ujs/wiki">jquery-ujs 的维基</a></li>
<li><a href="https://github.com/rails/jquery-ujs/wiki/External-articles">其他介绍 jquery-ujs 的文章</a></li>
<li><a href="http://www.alfajango.com/blog/rails-3-remote-links-and-forms/">Rails 3 远程链接和表单权威指南</a></li>
<li><a href="http://railscasts.com/episodes/205-unobtrusive-javascript">Railscasts: Unobtrusive JavaScript</a></li>
<li><a href="http://railscasts.com/episodes/390-turbolinks">Railscasts: Turbolinks</a></li>
</ul>
<h3>反馈</h3>
<p>
欢迎帮忙改善指南质量。
</p>
<p>
如发现任何错误,欢迎修正。开始贡献前,可先行阅读<a href="http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html#contributing-to-the-rails-documentation">贡献指南:文档</a>。
</p>
<p>翻译如有错误,深感抱歉,欢迎 <a href="https://github.com/ruby-china/guides/fork">Fork</a> 修正,或至此处<a href="https://github.com/ruby-china/guides/issues/new">回报</a>。</p>
<p>
文章可能有未完成或过时的内容。请先检查 <a href="http://edgeguides.rubyonrails.org">Edge Guides</a> 来确定问题在 master 是否已经修掉了。再上 master 补上缺少的文件。内容参考 <a href="ruby_on_rails_guides_guidelines.html">Ruby on Rails 指南准则</a>来了解行文风格。
</p>
<p>最后,任何关于 Ruby on Rails 文档的讨论,欢迎到 <a href="http://groups.google.com/group/rubyonrails-docs">rubyonrails-docs 邮件群组</a>。
</p>
</div>
</div>
</div>
<hr class="hide" />
<div id="footer">
<div class="wrapper">
<p>本著作采用<a href="https://creativecommons.org/licenses/by-sa/4.0/">创用 CC 姓名标示-相同方式分享 4.0 国际授权条款</a>授权。</p>
<p>“Rails”、“Ruby on Rails”,以及 Rails logo 为 David Heinemeier Hansson 的商标。版权所有。</p>
</div>
</div>
<script type="text/javascript" src="javascripts/jquery.min.js"></script>
<script type="text/javascript" src="javascripts/responsive-tables.js"></script>
<script type="text/javascript" src="javascripts/guides.js"></script>
<script type="text/javascript" src="javascripts/syntaxhighlighter/shCore.js"></script>
<script type="text/javascript" src="javascripts/syntaxhighlighter/shBrushRuby.js"></script>
<script type="text/javascript" src="javascripts/syntaxhighlighter/shBrushXml.js"></script>
<script type="text/javascript" src="javascripts/syntaxhighlighter/shBrushSql.js"></script>
<script type="text/javascript" src="javascripts/syntaxhighlighter/shBrushPlain.js"></script>
<script type="text/javascript">
SyntaxHighlighter.all();
$(guidesIndex.bind);
</script>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
// ga('create', '', 'ruby-china.github.io');
ga('require', 'displayfeatures');
ga('send', 'pageview');
</script>
</body>
</html>