RailsでのjavaScriptについて
このガイドでは、簡単にリッチなAjaxアプリケーションの構築を可能にする、 Rails組み込みのAjax/JavaScriptの機能について説明します。
このガイドを読むことで、次の事が学べるはずです。
- Ajaxの基本
- 控えめなJavaScript
- Railsの組み込みヘルパーはどのように手助けしてくれるのか。
- サーバーサイド上でのAjaxの扱い方について
- ターボリンクス(Turbolinks)Gemについて
1. Ajax入門
Ajaxを理解するには、始めにWebブラウザが通常何を行っているのかを知らなければいけません。
http://localhost:3000
と、ブラウザのアドレスバーに打ち込んでEnterを押すと、
ブラウザ(あなたの'クライアント')はサーバーへリクエストを行います。
その後、レスポンスを受け取りそれを解析し、JavaScript、スタイルシート、画像などの全ての関連するアセットを取得します。
そして、それによってページが組み立てられます。
リンクのクリックであってもプロセスは同じで、
ページを取得し、アセットを取得し、それをまとめた結果をあなたに対して表示します。
これは、'リクエスト・レスポンスサイクル'と呼ばれています。
また、JavaScriptもサーバへのリクエストを作成し、レスポンスを解析することが可能です。 そしてページ上の情報を更新する能力も持ちあわせています。 これら2つの力を繋げ、サーバからページ全体の取得を必要とする事無く、 JavaScriptはWebページの一部を更新する事が可能です。 このパワフルなテクニックを、我々はAjaxと呼んでいます。
RailsはデフォルトでCoffeeScriptを採用しているため、 このガイドのこの後のサンプルや例はCoffeeScriptになります。 これらのレッスン全ては、もちろん素(vanilla)のJavaScriptにも適用されます。
例として、下記はjQueryライブラリを使用してAjaxリクエストを作成しているCoffeeScriptになります。
$.ajax(url: "/test").done (html) ->
$("#results").append html
このコードは"/test"からデータを取得し、
results
のidを持つdiv
へ、その結果を追加します。
Railsはこのテクニックを使用した、Webページを構築するための、かなり多くの組み込みサポートを提供します。 あなたは少しだけ自身でこのコードを書く必要があります。 このガイドの残り部分では、Railsがどのようにこの方法でWebサイトの作成を助けてくれるかを説明しますが、 その全てがこの非常にシンプルなテクニックの上に構築されています。
2. 控えめなJavaScript
RailsはDOMへのJavaScript割り当てへの扱いに、"控えめなJavaScript"と呼ばれるテクニックを使用します。 これは通常フロントエンドコミュニティ内ではベストプラクティスと考えられていますが、 時折その他の方法を示したチュートリアルを読むことがあるかもしれません。
下記は最もシンプルに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>
厄介ですよね? 関数定義をクリックハンドラの外に出して、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-*
属性を追加し、
その属性を持つ書くリンクのクリックイベントにハンドラをバインドします。
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)
<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>
JavaScriptがHTMLと混ざらないことから、我々はこれを'控えめな'JavaScriptと呼びます。 関係性を適切に分割することで、機能変更を容易にします。 data属性を追加することで、リンクへの振る舞いの追加を容易にすることが出来ます。 圧縮・連結を通して、全てのJavaScriptを実行することが出来ます。 各ページ上でJavaScript全体のバンドル(ひとまとめにした物)を提供することが出来るため、 最初のページでそれをダウンロードすれば、それ以降の各ページではキャッシュされます。 これは総合的には利点が多いと考えられます。(翻訳に自信なし)
RailsチームはこのスタイルでCoffeeScript(とJavaScript)を書くことを強く推奨しており、 多くのライブラリがこのパターンに従っている事を期待することも出来ます。
3. 組み込みヘルパー
RailsはHTML生成を手助けするRubyによって書かれた多くのビューヘルパーメソッドを提供します。 それらの要素へAjaxの処理を少し加えたくなることがあり、Railsはそういったケースでそれを可能にしてくれます。
控えめなJavaScriptであるため、Railsの"Ajaxヘルパー"は実際には2つのパーツに別れます。 半分はJavaScriptで、半分はRubyです。
rails.jsはその半分のJavaScriptを提供し、 通常のRubyビューヘルパーはDOMへ適切なタグを追加します。 rails.jsのCoffeeScriptはこれらの属性をリッスンし、 適切なハンドラを割り当てます。
3.1 form_for
form_forはフォームを書くことを手助けするヘルパーです。
form_for
には:remote
オプションが用意されており、
それは次のように動作します。
<%= form_for(@post, remote: true) do |f| %>
...
<% end %>
これは下記のようなHTMLを生成します。
<form accept-charset="UTF-8" action="/posts" class="new_post" data-remote="true" id="new_post" method="post">
...
</form>
data-remote="true"
に注目してください。
このフォームはブラウザの通常のsubmitメカニズムによるものではなく、
Ajaxによるsubmitが行われます。
ですが、おそらくAjaxの処理が<form>
内に置かれて欲しくないと、考えるでしょう。
おそらく、submitが成功した際に何かを行いたいと、考えるでしょう。
それをするためには、ajax:success
イベントをバインドします。
失敗時にはajax:error
を使用します。
下記を確認してみてください。
$(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>"
より洗練されたコードにしたいとお思いでしょうが、ここから始めていきます。 イベント詳細については、 jquery-ujs wikiを参照してください。
3.2 form_tag
form_tagはform_for
に非常に似ています。
:remote
オプションを持ち、次のように使用することが出来ます。
<%= form_tag('/posts', remote: true) do %>
...
<% end %>
これは下記のHTMLを生成します。
<form accept-charset="UTF-8" action="/posts" data-remote="true" method="post">
...
</form>
その他はform_for
と同じです。
詳細を知りたければ、そのドキュメントを参照してください。
3.3 link_to
link_to
は、リンク生成の手助けをするヘルパーです。
:remote
オプションを持ち、次のように使用することが可能です。
<%= link_to "a post", @post, remote: true %>
これは下記を生成します。
<a href="/posts/1" data-remote="true">a post</a>
form_for
と同じAjaxイベントをバインドする事が出来ます。
これから、その例を示します。
クリックするだけで削除することが出来るpost(投稿)のリストを持っていると仮定してみましょう。
まず、次のようにしてHTMLを生成し、
<%= link_to "Delete post", @post, remote: true, method: :delete %>
下記のようなCoffeeScriptを書きます。
$ ->
$("a[data-remote]").on "ajax:success", (e, data, status, xhr) ->
alert "The post was deleted."
3.4 button_to
button_to
は、ボタン生成を手助けするヘルパーです。
これは次のような呼び出しを行うことが出来る:remote
オプションを持ちます。
<%= button_to "A post", @post, remote: true %>
これは次のように生成されます。
<form action="/posts/1" class="button_to" data-remote="true" method="post">
<div><input type="submit" value="A post"></div>
</form>
まさに<form>
であるため、form_for
の情報も全て適用されます。
4. サーバーサイドに関する事について
Ajaxはクライアントサイドだけで完結するものでは無いため、これをサポートするためのサーバーサイド側の処理も必要になります。 多くの人は、AjaxリクエストがHTMLよりもJSONを返すことを好みます。 これを実現するために必要なことを考えていきましょう。
4.1 シンプルな例
あなたは一連のユーザーを持ち、それを表示するのと同じページ上で新しいユーザーを作成するフォームを提供したい、という状況を想像してください。 コントローラーのindexアクションは次のようになります。
class UsersController < ApplicationController
def index
@users = User.all
@user = User.new
end
# ...
indexのビュー(app/views/users/index.html.erb
)は下記を含みます。
<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 %>
app/views/users/_user.html.erb
のpartialは、下記を含みます。
<li><%= user.name %></li>
indexページの上部には、ユーザーが表示されます。 下部には、新しいユーザーを作成するフォームが提供されます。
下部のフォームは、UsersController
のcreate
アクションを呼び出します。
フォームのremote
オプションはtrue
に設定されてるため、
リクエストはAjaxリクエストとしてUsersController
へPOSTされます。
このリクエストを提供するには、コントローラーの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
に注目してください。
これは、コントローラーがAjaxリクエストに応答することを許可します。
次に、対応するapp/views/users/create.js.erb
ビューのファイルを用意します。
このファイルが実際に使用されるJavaScriptコードを生成し、それがクライアント上に送られて実行されます。
$("<%= escape_javascript(render @user) %>").appendTo("#users");
5. Turbolinks
Rails4には、 TurbolinksのGemが含まれます。 このGemはAjaxを使用し、多くのアプリケーション内でページ描画のスピードを向上させます。
5.1 Turbolinksの動作方法
Turbolinksはページ上の全ての<a>
に、クリックハンドラを割り当てます。
もしブラウザがPushStateをサポートする場合、
TurbolinksはAjaxリクエストを作り、レスポンスを解析し、<body>
の応答により、
ページの<body>
全体を置き換えます。
これは、PushStateを使用してURLを正しいものに変更することで、
セマンティクスの供給を保持し、整理された一連のURLを提供します。
Turbolinksを有効にするために行うことは、Gemfileにそれを持たせるだけなので、
通常はapp/assets/javascripts/application.js
ファイル内に指定されている、
CoffeeScriptマニフェストに//= require turbolinks
を配置します。
もし特定のリンクのTurbolinksを無効にしたければ、
data-no-turbolink
属性をタグに追加します。
<a href="..." data-no-turbolink>No turbolinks here</a>.
5.2 ページ変更イベント
CoffeeScriptを書いている際に、ページ読み込み時に何らかのプロセスを実行したいというケースがよくあります。 jQueryを使用して、次のように書く事が出来ます。
$(document).ready ->
alert "page has loaded!"
ただし、Turbolinksは通常のページ読み込み時のプロセスを上書きしてしまうため、 これに依存するイベントがトリガ(発火)されません。 もし、そのようなコードがある場合は、 代わりに下記のように変更しなければいけません。
$(document).on "page:change", ->
alert "page has loaded!"
詳細についてはその他のイベントのバインドについても解説されている、 Turbolinks READMEを参照してください。
6. その他のリソースについて
下記に、より学習を進めていく上で役立つリンクを紹介しておきます。
© 2010 - 2017 STUDIO KINGDOM