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_tagform_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ページの上部には、ユーザーが表示されます。 下部には、新しいユーザーを作成するフォームが提供されます。

下部のフォームは、UsersControllercreateアクションを呼び出します。 フォームの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");

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. その他のリソースについて

下記に、より学習を進めていく上で役立つリンクを紹介しておきます。

 Back to top

© 2010 - 2017 STUDIO KINGDOM