Posts RailsでAjax処理を実装
Post
Cancel

RailsでAjax処理を実装

Rails に Ajaxを実装するいくつかの方法をまとめておこうと思います。

  • jQueryでAjaxを実装 – validation で利用
  • RailsでのAjax実装 1 – form_for でAjax
  • RailsでのAjax実装 2 – button_to でAjax

jQueryでAjaxを実装

jQueryでvalidationを行うのに「jQuery Validation Plugin」を読み込むようにし、Ajaxで処理するには remote メソッドを使います。

具体的にここでは、インプットタグに入力したユーザーのログインIDが既に登録されているかどうかのバリデーションをAjaxで処理します。

jQueryでAjax処理を行い UserController#check_loginid メソッドへ、インプットタグに入力された loginid と編集しているユーザーのuser.id (新規であれば -1 )を送りその結果を返します。レスポンスはJSONで返す必要があります。

応答が true であればバリデートを通り、 false であればバリデートメッセージが表示されます。

File: app/views/users/_form.html.erb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<script>
    $(document).ready(function () {
        $('form[id*="user"]').validate({
            rules: {
                'user[loginid]': {
                    required: true,
                    remote: {
                        url: "<%= root_path %>users/check_loginid",
                        type: "POST",
                        async: true,
                        dataType: "json" ,
                        data: {
                            loginid: function () {
                                return $("input#user_loginid").val();
                            },
                            user_id: <%= @user.id ? @user.id : -1 %>
                        }
                    },
                    maxlength: 64
               }
            }
        });
    });
</script>

url:のコントローラメソッドに値を送る変数がdata:にあるloginiduser_idで、コントロ-ラ側ではparams['loginid']params['user_id']として値を受け取ります。

コントローラでの処理結果はJSONで返します(dataType: "json"および下記コード15行目)。

File: app/controllers/users_controller.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def check_loginid
   result = nil
   valid  = false
   if params['loginid'].present? && params['user_id'].present?
     if params['user_id'].to_i > 0
       result = User.where(loginid: params['loginid']).where.not(id: params['user_id'])
     else
       result = User.where(loginid: params['loginid'])
     end
     unless result.present?
       valid = true
     end
   end

  render :json => valid
end

routes.rb にルートを追加します。

File: config/routes.rb

1
  post 'users/check_loginid' , as: 'users_check_loginid', to: 'users#check_loginid'

RailsでAjaxを実装

Railsのお約束

  • ビューのテンプレート hoge.html.erb と Ajax 用テンプレート hoge.js.erb を同じ名前(hogeの部分)で作成する。
  • ビューの form_forbutton_to(やlink_to) に :remote => true(あるいはremote: true、またdata: {remote: true}の方がよいという話もありますが…) を記述する。

これでAjax化できます。

Ajaxを用いて画面全体ではなく、一部分の表示を切り替えます。Ajaxからの応答を受けて、表示する場所や文言などをJavaScriptで記述するのがAjax用テンプレートです。

以下2つの例をあげます。一番目はフォームから送られる値によって <div> タグ部分の表示を変え、二番目は画面内にある複数のボタンのどれを押したかで表示を変えますがHTMLの一部分を別ファイルに分けたものを読み込みなおして表示を変えてみます。

form_for でAjax

ここでは、ビューのテンプレートを ajax_test1.html.erb、Ajax 用テンプレートを ajax_test1.js.erb として作成します。 ajax_test1.html.erb の中に一行 <div id="ajax_display_test"></div> があり、その場所に表示する文言をJavaScriptで変更します。このようにAjax通信で返されてくる値を受け取りビューに反映させるためのJavaScriptを ajax_test1.js.erb に書きます。

編集するファイル

Ajax 用テンプレート

Ajaxによってレスポンスが返ってきた時に読み込まれるので、このファイルが読み込まれるときにはJsonで送られてきた値(この例では @result )は存在しています。

File: app/views/users/ajax_test1.js.erb

1
2
3
4
5
if (<%= @result %> ) {
    $("#ajax_display_test").html("正解!おめでとう!");
} else {
    $("#ajax_display_test").html("残念でした。今度は頑張ってね。");
}

ビュー用テンプレート

form_for:remote => true とすることで、Ajaxで値を送信することができます。

File:app/views/users/ajax_test1.html.erb

1
2
3
4
5
6
7
8
9
10
11
12
<%=  form_for('ajax_test1', :method => :post, :remote => true, :id => "ajax_test1_form") do %>
    <div class="field">
      <p>Railsを動かすプログラミング言語は?</p>
      <%= text_field_tag 'ajax_test[answer]' %>
    </div>

    <div id="ajax_display_test"></div>

    <div class="actions">
      <%= submit_tag '解答する' %>
    </div>
<% end %>

コントローラ

正しい解答が送られてきたら @result = true とします。

File:app/controllers/users_controller.rb {: .filename}

1
2
3
4
5
6
7
8
def ajax_test1
   @result = false
   if params['ajax_test'].present?
      if params['ajax_test']['answer'] == 'Ruby'
        @result = true
      end
   end
end

routes.rb

ルートを追加します。

File: config/routes.rb

1
match 'ajax/test1' , as: 'ajax_test1', to: 'users#ajax_test1' ,via: [:get, :post]

form_for にPOSTで送信するよう指定していたのでGET、POST両方のルートが必要です。

button_to でAjax

2つ目は、複数のボタンタグがありボタンごとに異なる値を送信する場合を作ってみます。さらに、帰ってきた結果をJavaScriptで(先ほどのように)書き換えてもよいのですが、今度はパーシャル(部分テンプレート)を読み込みなおして表示を変更しようと思います。

Railsのお約束

  • パーシャル(部分テンプレート)として使うファイル名の先頭には _ (アンダースコア)を付ける。

編集するファイル

パーシャル

パーシャル(部分テンプレート)内に渡されてくるローカル変数を data とします。このローカル変数については後述します。

File:app/views/users/_partial_ajax.html.erb

1
2
3
4
5
6
7
8
9
<p>
  <strong>ログインID:</strong>
  <%= data.loginid %>
</p>

<p>
  <strong>パスワード:</strong>
  <%= data.password %>
</p>

Ajax用テンプレート

Ajax通信のレスポンスの時に読み込まれるのでAjaxでない時にはパーシャルは読み込まれません。

File:app/views/users/ajax_test2.js.erb

1
    $("#ajax_user").html("<%= j(render :partial => 'users/partial_ajax', :locals => {:data => @user_data}) %>");
j()
escape_javascript()のエイリアス。キャリッジリターン、シングル(ダブル)コーテーションなどをエスケープします(http://api.rubyonrails.org/を参照)。
render :partial => 'users/partial_ajax'
読み込むパーシャル(部分テンプレート)を指定します。ファイル名の先頭の_は記述しません。また同じディレクトリにあるパーシャルを指定するときにはディレクトリ名を省略できます。
@user_data
Ajaxによってコントローラから送られてきたユーザーのデータです。
:locals => {:data => @user_data}
パーシャルに与えるローカル変数を指定しているところです。パーシャル内でdataによって@user_dataにアクセスできます。

パーシャル内でも@user_dataにはそのままアクセスできますが、:localsにまとめて記述しておくとパーシャルに渡して使う変数が一目でわかるので見通しがよいと思います。

ビュー用テンプレート内の<div id="ajax_user"></div>と記述した場所にパーシャルが読み込まれます。

ビュー用テンプレート

左側にユーザー一覧を表示しユーザーごとにボタンを付け、押されたボタンがAjax通信をしてデータベースからユーザー情報を取得します。その結果を表示する場所が下のコード「1行目」ですが、少し右下に移動させて一覧の右側に並ぶようにしています。

File:app/views/users/ajax_test2.html.erb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<div id="ajax_user" style="position: absolute; left: 10cm;top: 5cm;"></div>

<div>
  <table>
    <thead>
    <tr>
      <th>ユーザー一覧</th>
      <th></th>
    </tr>
    </thead>

    <tbody>
    <% @users.each do |user| %>
        <tr>
          <td><%= user.loginid %></td>
          <td><%= button_to '表示', ajax_test2_path(:user_id => user.id), :method => :get, :remote => true %></td>
        </tr>
    <% end %>
    </tbody>
  </table>
</div>

「16行目」のbutton_to:remote => trueを記述することでAjax通信が可能になり、ajax_test2_path:user_id => user.idを送信します。

ajax_test2_pathで指定されたコントローラメソッドが、user_idを受け取り、データベースから取得したユーザー情報を返します。

コントローラ & routes.rb

File:app/controllers/users_controller.rb

1
2
3
4
5
6
7
def ajax_test2
  if params['user_id'].present?
    @user_data = User.find(params['user_id'])
  else
    @users = User.all
  end
end

File:config/routes.rb

1
get 'ajax/test2' , as: 'ajax_test2', to: 'users#ajax_test2'

button_to:method => :getと指定したのでルートはGETだけです。POSTを指定したらルートには両方が必要になります。

終わり

シェア
#内容発言者

Jekyll - Pluginを作る

プログラミング基礎知識 - Linuxの基本コマンド