-
Notifications
You must be signed in to change notification settings - Fork 0
/
working_with_javascript_in_rails.html
551 lines (494 loc) · 32.1 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
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>在 Rails 使用 JavaScript — Ruby on Rails 指南</title>
<meta name="description" content="Ruby on Rails 指南:系統學習 Rails(Rails 4.2 版本)" >
<meta name="keywords" content="Ruby on Rails Guides 指南 中文 學習 免費 網路 Web 開發" >
<meta name="author" content="http://git.io/G_R1sA">
<meta property="fb:admins" content="1340181291">
<meta property="og:title" content="在 Rails 使用 JavaScript — Ruby on Rails 指南" >
<meta property="og:site_name" content="Ruby on Rails 指南">
<meta property="og:image" content="http://rails.ruby.tw/images/rails_guides_cover.jpg">
<meta property="og:url" content="http://rails.ruby.tw/">
<meta property="og:type" content="article">
<meta property="og:description" content="Ruby on Rails 指南:系統學習 Rails(Rails 4.2 版本)">
<link rel="stylesheet" href="stylesheets/application.css">
<link href="http://fonts.googleapis.com/css?family=Noto+Sans:400,700|Noto+Serif:700|Source+Code+Pro" rel="stylesheet">
<link href="images/favicon.ico" rel="shortcut icon" type="image/x-icon">
</head>
<body class="guide">
<div id="fb-root"></div>
<script>(function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/zh-TW/sdk.js#xfbml=1&appId=837401439623727&version=v2.0";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));</script>
<script type="text/javascript">
window.twttr=(function(d,s,id){var t,js,fjs=d.getElementsByTagName(s)[0];if(d.getElementById(id)){return}js=d.createElement(s);js.id=id;js.src="https://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);return window.twttr||(t={_e:[],ready:function(f){t._e.push(f)}})}(document,"script","twitter-wjs"));
</script>
<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>Models</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>Views</dt>
<dd><a href="layouts_and_rendering.html">Rails 算繪與版型</a></dd>
<dd><a href="form_helpers.html">Action View 表單輔助方法</a></dd>
<dt>Controllers</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">Rake 任務與 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>
<dt>Rails 指南翻譯術語</dt>
<dd><a href="translation_terms.html">翻譯術語</a></dd>
</dl>
</div>
</li>
<li><a class="nav-item" href="//github.com/docrails-tw/guides">貢獻翻譯</a></li>
<li><a class="nav-item" href="contributing_to_ruby_on_rails.html">貢獻</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="Models">
<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="Views">
<option value="layouts_and_rendering.html">Rails 算繪與版型</option>
<option value="form_helpers.html">Action View 表單輔助方法</option>
</optgroup>
<optgroup label="Controllers">
<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">Rake 任務與 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>
<optgroup label="Rails 指南翻譯術語">
<option value="translation_terms.html">翻譯術語</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 與 HTML 分離(Unobtrusive 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-%E4%BB%8B%E7%B4%B9">Ajax 介紹</a></li>
<li><a href="#unobtrusive-javascript">Unobtrusive JavaScript</a></li>
<li>
<a href="#%E5%85%A7%E5%BB%BA%E7%9A%84-ajax-%E5%B9%AB%E5%8A%A9%E6%96%B9%E6%B3%95">內建的 Ajax 幫助方法</a>
<ul>
<li><a href="#form-for">form_for</a></li>
<li><a href="#form-tag">form_tag</a></li>
<li><a href="#link-to">link_to</a></li>
<li><a href="#button-to">button_to</a></li>
</ul>
</li>
<li>
<a href="#%E4%BC%BA%E6%9C%8D%E5%99%A8%E7%AB%AF%E7%9A%84%E8%80%83%E9%87%8F">伺服器端的考量</a>
<ul>
<li><a href="#%E7%B0%A1%E5%96%AE%E7%9A%84%E4%BE%8B%E5%AD%90">簡單的例子</a></li>
</ul>
</li>
<li>
<a href="#turbolinks">Turbolinks</a>
<ul>
<li><a href="#turbolinks-%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86">Turbolinks 工作原理</a></li>
<li><a href="#%E9%A0%81%E9%9D%A2%E8%AE%8A%E5%8C%96%E7%9A%84%E4%BA%8B%E4%BB%B6">頁面變化的事件</a></li>
</ul>
</li>
<li><a href="#%E5%85%B6%E4%BB%96%E8%B3%87%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>,並按下 Enter。瀏覽器此時便向伺服器發送請求。伺服器接收請求,去拿所有需要的資源(assets),像是 JS、CSS、圖片等,接著將這些資源,按照程式邏輯組合成網頁,返回網頁給瀏覽器。在網頁裡按下某個連結,會重複剛剛的步驟:發送請求、抓取資源、組合頁面、返回結果。這幾個步驟通常稱為“請求響應週期”(Request Response Cycle)。</p><p>JavaScript 也可向伺服器發送請求,並解析響應。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>id</code> 為 <code>#results</code> 的 <code>div</code> 之後。</p><p>Rails 對於使用這種技巧來撰寫網頁,提供了相當多的官方支援。幾乎很少會需要自己寫這樣的程式。以下章節將示範,如何用點簡單的技術,便能用 Rails 寫出應用了 Ajax 的網站。</p><h3 id="unobtrusive-javascript">2 Unobtrusive JavaScript</h3><p>Rails 使用一種叫做 “<a href="http://zh.wikipedia.org/zh-tw/Unobtrusive_JavaScript">Unobtrusive JavaScript</a>” (縮寫為 UJS)的技術來處理 DOM 操作。這是來自前端社群的最佳實踐,但有些教學文件可能會用別種技術,來達成同樣的事情。</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>按下連結,背景就變紅。如果按下連結後,要執行許多 JavaScript 程式碼怎麼辦?</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>尷尬吧?可以將 JavaScript 抽離出來,並用 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> 屬性,接著給每個連結的 click 事件,加上一個處理函式:</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 (e) ->
e.preventDefault()
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>這個技術稱為 “Unobtrusive” JavaScript。因為 JavaScript 不再需要與 HTML 混在一起。之後便更容易修改,也更容易加新功能上去。任何連結只要加個 <code>data-</code> 屬性,便可以得到同樣效果。將 JavaScript 從 HTML 抽離後,JavaScript 便可透過合併壓縮工具,讓所有頁面可以共用整份 JavaScript 。也就是說,只需在第一次戴入頁面時下載一次,之後的頁面使用快取的檔案即可。Unobtrusive JavaScript 帶來的好處非常多。</p><p>Rails 團隊強烈建議採用這種風格來撰寫 CoffeeScript (JavaScript),你會發現許多函式庫也採用這種風格。</p><h3 id="內建的-ajax-幫助方法">3 內建的 Ajax 幫助方法</h3><p>Rails 在 View 提供了許多用 Ruby 寫的幫助方法來產生 HTML。會想元素加上 Ajax?沒問題,Rails 會幫助你。</p><p>Rails 的 “Ajax 幫助方法” 實際上分成用 JavaScript 所寫的幫助方法,與用 Ruby 所寫成的幫助方法。</p><p>用 JavaScript 寫的部分可以在這找到 <a href="https://github.com/rails/jquery-ujs/blob/master/src/rails.js">rails.js</a>,而用 Ruby 寫的部份就是 View 的幫助方法,用來給 DOM 新增適當的標籤。rails.js 裡的 CoffeeScript 會監聽這些屬性,執行相應的處理函式。</p><h4 id="form-for">3.1 form_for</h4><p><a href="http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_for"><code>form_for</code></a></p><p>撰寫表單的幫助方法。接受 <code>:remote</code> 選項:</p><div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false">
<%= form_for(@article, 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="/articles" class="new_article" data-remote="true" id="new_article" method="post">
...
</form>
</pre>
</div>
<p>注意 <code>data-remote="true"</code>。有了這個屬性之後,表單會透過 Ajax 提交,而不是瀏覽器平常的提交機制。</p><p>除了產生出來的 <code><form></code> 之外,可能還想在提交成功與失敗做某些處理。可以透過 <code>ajax:success</code> 與 <code>ajax:error</code> 事件,在提交成功與失敗時,來附加內容至 DOM:</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$(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>"
</pre>
</div>
<p>當然這只是個開始,更多可用的事件可在 <a href="https://github.com/rails/jquery-ujs/wiki/ajax">jQuery-ujs 的維基頁面</a>上可找到。</p><h4 id="form-tag">3.2 form_tag</h4><p><a href="http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html#method-i-form_tag"><code>form_tag</code></a></p><p>跟 <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('/articles', remote: true) %>
</pre>
</div>
<p>產生的 HTML:</p><div class="code_container">
<pre class="brush: xml; gutter: false; toolbar: false">
<form accept-charset="UTF-8" action="/articles" data-remote="true" method="post">
...
</form>
</pre>
</div>
<h4 id="link-to">3.3 link_to</h4><p><a href="http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to"><code>link_to</code></a></p><p>產生連結的幫助方法。接受 <code>:remote</code> 選項:</p><div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false">
<%= link_to "an article", @article, remote: true %>
</pre>
</div>
<p>產生的 HTML:</p><div class="code_container">
<pre class="brush: xml; gutter: false; toolbar: false">
<a href="/artciles/1" data-remote="true">an article</a>
</pre>
</div>
<p>可以像上面 <code>form_for</code> 例子那樣,綁定相同的 Ajax 事件上去。 來看個例子,假設按個按鍵,刪除一篇文章,提示一些訊息。只需寫一些 HTML:</p><div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false">
<%= link_to "Delete artcile", @article, 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 article was deleted."
</pre>
</div>
<p>就這麼簡單。</p><h4 id="button-to">3.4 button_to</h4><p><a href="http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-button_to"><code>button_to</code></a></p><p>建立按鈕的幫助方法。接受 <code>:remote</code> 選項:</p><div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false">
<%= button_to "An article", @article, remote: true %>
</pre>
</div>
<p>會產生:</p><div class="code_container">
<pre class="brush: xml; gutter: false; toolbar: false">
<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>
</pre>
</div>
<p>由於這只是個 <code><form></code>,所有 <code>form_for</code> 可用的東西,也可以應用在 <code>button_to</code>。</p><h3 id="伺服器端的考量">4 伺服器端的考量</h3><p>Ajax 不只是客戶端的事,伺服器也要出力。人們傾向 Ajax 請求回傳 JSON,而不是 HTML,來看看如何回傳 JSON。</p><h4 id="簡單的例子">4.1 簡單的例子</h4><p>假設有許多使用者,想給他們顯示建立新帳號的表單。而 Controller 的 <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> View (<code>app/views/users/index.html.erb</code>):</p><div class="code_container">
<pre class="brush: plain; 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> Partial:</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>Users</code> Controller 的 <code>create</code> 動作。因為表單有 <code>remote: true</code> 這個選項,請求會使用 Ajax POST 到 <code>Users</code> Controller,等待 Controller 回應 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>,這是 Controller 回應 Ajax 請求的地方。<code>create</code> 動作對應 <code>app/views/users/create.js.erb</code>:</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 RubyGem</a>。Turbolinks 使用了 Ajax 技術,可以加速頁面的渲染。</p><h4 id="turbolinks-工作原理">5.1 Turbolinks 工作原理</h4><p>Turbolinks 給頁面上所有的 <code>a</code> 標籤添加了一個 click 處理函式。如果瀏覽器支援 <a href="https://developer.mozilla.org/en-US/docs/DOM/Manipulating_the_browser_history#The_pushState().C2.A0method">PushState</a>,Turbolinks 會對頁面發出 Ajax 請求,解析伺服器回過來的響應,把頁面整個 <code><body></code> 用響應回傳的 <code><body></code> 換掉。接著 Turbolinks 會利用 PushState 把 URL 換成正確的,看起來就像重新整理一樣,仍保有漂亮的 URL。</p><p>啟用 Turbolinks 只需在 <code>Gemfile</code> 加入:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
gem 'turbolinks'
</pre>
</div>
<p>並在 CoffeeScript Manifest 檔案(<code>app/assets/javascripts/application.js</code>)裡加入:</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
//= require turbolinks
</pre>
</div>
<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 覆寫了頁面加載邏輯,依賴 <code>$(document).ready</code> 事件的程式碼不會被觸發。若是寫了類似上例的程式碼,必須改寫成:</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 wiki</a></li>
<li><a href="https://github.com/rails/jquery-ujs/wiki/External-articles">jquery-ujs list of external articles</a></li>
<li><a href="http://www.alfajango.com/blog/rails-3-remote-links-and-forms/">Rails 3 Remote Links and Forms: A Definitive Guide</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/docrails-tw/guides/fork">Fork</a> 修正,或至此處<a href="https://github.com/docsrails-tw/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/deed.zh_TW">創用 CC 姓名標示-相同方式分享 4.0 國際授權條款</a>授權。</p>
<p>“Rails”、“Ruby on Rails”,以及 Rails logo 為 David Heinemeier Hansson 的商標。版權所有。</p>
</div>
</div>
<script src="javascripts/jquery.min.js"></script>
<script src="javascripts/responsive-tables.js"></script>
<script src="javascripts/guides.js"></script>
<script src="javascripts/syntaxhighlighter/shCore.js"></script>
<script src="javascripts/syntaxhighlighter/shBrushRuby.js"></script>
<script src="javascripts/syntaxhighlighter/shBrushXml.js"></script>
<script src="javascripts/syntaxhighlighter/shBrushSql.js"></script>
<script 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", "UA-49903900-1", "auto");
ga("require", "displayfeatures");
ga("send", "pageview");
</script>
</body>
</html>