Rails4で簡単Ajax

RailsアプリケーションでAjaxを実装する際にかなり躓いたので手順のメモ。

巷では、formでremote: trueをするだけの簡単なお仕事だと言われているけど、流れが分かっていないと存外難しいように思う。
実際問題、大体の仕組みさえわかれば、jQueryajax要求出す必要もないし、とても簡便にajaxが実装出来るようになっていると思う。
というわけで早速復習。

環境

Ruby 2.1.1
Rails 4.0.3

サンプルの制作

今回はとりあえずメッセージの投稿だけを行う簡易アプリを作り、Ajax化する。
scaffoldは便利だけど、この程度の規模の場合は使わないほうが明解だと思うので、一つ一つ手作りしていく。

何はともあれ新規プロジェクトを作成し、モデルおよびコントローラの作成。

rails new Bwitter
cd Bwitter
rails g model bweet description:text
rails g controller bweets index create
rake db:create
rake db:migrate
rails s

いつもモデルで指定する型とかを忘れてしまう。いつになったら覚えられるのか……。
とりあえずモデルはメッセージ格納のためのtext型descriptionというカラムを作成し、コントローラーは投稿されたメッセージ一覧を表示するためのindexアクションと、メッセージのDB格納用にcreateアクションの二つを制作。多分最小構成だと思う。

サーバー起動後localhost:3000にアクセスすると親しみ深い画面が。
f:id:taka_say:20140310173053p:plain


まずはルーティングから。
恐らく初期状態だとgetメソッドのindexとcreateがあると思われるので、

#config/routes.rb
root :to => 'bweets#index'
get "bweets/index"
post "bweets/create"

という感じで、rootアクションをindexに変更およびcreateのメソッドをpostに変更。


次にindexアクションの編集。コントローラから。

#app/controllers/bweets_controller.rb
def index
  @bweets = Bweet.order("created_at DESC").limit(10)
end

とりあえず、新しい投稿を10件取得する感じで。

RailsAjaxを簡単に実装するためには、Ajaxで表示したい要素を、部分テンプレートで作成するのがコツっぽい。
indexでは、投稿されたメッセージの一覧表示をAjax化したいため、ここを部分テンプレートで作成する。
というわけで、app/views/bweets以下に、_bweet.html.erbという部分テンプレートファイルを新規作成する。

#app/views/bweets/_bweet.html.erb
<% @bweets.each do |bweet| %>
  <fieldset>
    <legend><%= bweet.created_at %></legend>
    <p><%= bweet.description %></p>
  </fieldset>
<% end %>

表示を手抜くためにfieldsetを使っているけど、大した意味は無い。

実は部分テンプレートの中では、render時に持ってきたインスタンス変数を@省略のファイル名で利用できたり、コレクションのループがされたりと、色々複雑っぽい。とりあえずはこれで、理解を深めたら追々使いこなしていきたい。


部分テンプレートは出来たので、index.html.erbの中に埋め込んであげればOK。
せっかくなので投稿フォームも一緒に作成。
投稿フォームはTwitter風に、index画面の横側に表示。

<div id='wrap'>
  <div id='sideWrap'>
    <%= form_tag( { action: :create }, { remote: true } ) do %>
  <fieldset>
    <legend>Bweet</legend>
    <div><%= text_area_tag :description%></div>
    <div><%= submit_tag :Bweet %></div>
  </fieldset>
  <% end %>
  </div><!-- /sideWrap -->
  <div id='mainWrap'>
    <h1>BweetsList</h1>
    <div id='bweets'>
      <%= render partial: "bweets", collections: @bweets %>
    </div>
  </div><!-- /mainWrap -->
</div><!-- /wrap -->

divは後で多少体裁を整えるためだけなので、基本的には気にする必要はなし。
ただ、部分テンプレートの呼び出しを行っている部分のdivのidは、Ajaxでの要素入れ替えで利用するので、設定の必要がある。

部分テンプレートの呼び出しは、renderのpartialオプションで指定する。同じく変数はcollectionsで渡す。
form_tagではオプションとして、remote: trueを設定する。
実際問題formのAjax化はこのオプションだけで完了。
ちなみにform_tagのオプションは、1つ目のハッシュがアクションの指定、2つ目のハッシュがその他のオプションの指定という感じだったと思う。結構ここでもエラーを吐かれた。


表示の部分は大凡出来たので、次はcreateの方を作る。
createができないと表示の確認すら出来ない(DB直接叩けば可能ではあるが……)。

#app/controller/bweets_controller.rb
def create
  @bweets = Bweet.order("created_at DESC").limit(10)
  @bweet = Bweet.new(bweet_params)
  respond_to do |format|
    if @bweet.save
      format.js
    else
      render "index"
    end
  end
end

private
def bweet_params
  params.permit(:description)
end

createでは、送信されたパラメータを取得し、bweetの新規作成と保存という、一般的な格納の手順をそのまま踏んでいる。
一つ特殊なのが、respond_toのformatにformat.jsを指定していること。
これを用意することで、後述するcreate.js.erbの呼び出しが行われる。

privateで定義しているbweet_paramsメソッドは、Rails4で導入されたstrong_parametersのために定義しているだけ。ここでは詳しく述べない。

最初のbweet10件取得は、後の描写時に@bweetsの値が必要なため。もっとスマートなやり方はいくらでもありそう。

とりあえず現状でformに値を入れsubmitすると、特に表示の変化が起きない事が確認出来ると思う。
更新を押すと、投稿したメッセージが現れるはず。
f:id:taka_say:20140310184121p:plain
cssでfloatの設定だけ行った


先ほどcreate内のformatでjsを指定したことで、createアクション時にcreate.js.erbが呼ばれるようになった。
app/views/bweets以下に、create.js.erbを新規作成し、Ajaxで処理をしたい、bweetの表示部分テンプレートの呼び出しを記述する。

$('#bweets').html("<%=j render partial: 'bweet', collections: @bweet %>");
$('#description').val("");

こんな感じ。
jはjavascriptで云々かんぬんなメソッドらしい。
f:id:taka_say:20140310185104p:plain
画像では表現出来ないけど、無事投稿と同時に、BweetsListに新規投稿が表示されたのではないかと思う。

まとめ

復習がてらに簡単なAjaxアプリを作ってみたけど、再度の作業でも若干の躓きがあった。
Rails自体にあまり慣れていないのと併せて、結構辛い。
Rails4からはturbolinksなるものが標準搭載されているので、jQueryを使う場合は競合に気を使ったりする必要がある様子。今回のAjax化手法の場合は、特段turbolinksに関する設定は弄らず実装できた。
予想外に記事は長くなるし、要旨も纏まってないしで最悪ではあるけど、ともかく、RailsAjaxもすごいね!ってことで誤魔化す。

・余談
RailsAjaxスタイルは、恐らくこの形が最も簡単なのではないかと思うが、一応jQueryajaxメソッドを使ったAjax化も、後々手順をメモるつもり……いつの日か……。

参考

RailsによるアジャイルWebアプリケーション開発 第4版
をベースに作成しました。

Ruby on Rails 3 の Ajax いろいろ - @yuumi3のお仕事日記
form_tag - リファレンス - Railsドキュメント

その他色々Webサイト
(書き記せないくらい膨大な数のページを参考にしました。)