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:
にあるloginid
とuser_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_for
やbutton_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用テンプレート app/views/users/ajax_test1.js.erb
- ビュー用テンプレート app/views/users/ajax_test1.html.erb
- コントローラ app/controllers/users_controller.rb
- ルート config/routes.rb
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のお約束
- パーシャル(部分テンプレート)として使うファイル名の先頭には
_
(アンダースコア)を付ける。
編集するファイル
- パーシャル app/views/users/_partial_ajax.html.erb
- Ajax用テンプレート app/views/users/ajax_test2.js.erb
- ビュー用テンプレート app/views/users/ajax_test2.html.erb
- コントローラ app/controllers/users_controller.rb
- ルート config/routes.rb
パーシャル
パーシャル(部分テンプレート)内に渡されてくるローカル変数を 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を指定したらルートには両方が必要になります。
終わり