はじめに
RailsでのAjax処理、Bootstrapとの組み合わせについては結構記事が見つかりますが、情報が古いものが引っかかる事も割とあります。そんな中久しぶりにこの組み合わせをRails5ベースの最新状態で試してみたので、自分の復習とメモがてらこの記事を残しておきます。
2018/06/25追記修正
@kozimon 様から頂いたコメントを取りこみ、Bootstrap4バージョンの記述を追記しました。コメントありがとうございます。
道筋
- いつも通りRailsでアプリを作ります。
- Bootstrapを導入します。ViewはSCSSベースとします。
- 簡単なアプリをscaffoldで作成します。
- Bootstrapの確認
- Bootstrapを用いて、新規作成フォームをModal化します。
- 新規作成処理をAjax化します。
- 一覧の更新をAjax化します。
という流れで進んでいきます。
実際の手順
Railsで新規アプリの作成
いつも通り、railsのnewコマンドでアプリを作成します。適当なディレクトリで実行します。
$ rails new modal-ajax
実行し終わったら、作成されたディレクトリにcdで移動しておきます。
$ cd modal-ajax
Bootstrapの導入(Bootstrap3まで)
Gemfileに以下の一行を加えます
# for Bootstrap3
gem 'bootstrap-sass'
2018/06/25 追記
@kozimon 様からコメント頂きました。Bootstrap4では以下の様な記述になるとのこと
# for Bootstrap4
gem 'jquery-rails'
gem 'bootstrap', '~> 4.1.1'
書き加えたら、当該フォルダでbundle installを実行します。
$ bundle install
RailsファイルのBootstrap用調整
まずは、app/assets/stylesheets配下にあるapplication.cssをapplication.css.scss(ややこしい)にリネームします。
$ mv app/assets/stylesheets/application.css app/assets/stylesheets/application.css.scss
リネームし終わったら、application.css.scssを開いて以下の行を追加します。
// for Bootstrap3
@import "bootstrap-sprockets";
@import "bootstrap";
2018/06/25 追記
@kozimon 様からコメント頂きました。Bootstrap4では以下の様な記述になるとのこと。
@import "bootstrap-sprockets";
が不要となるようです。。
// for Bootstrap4
@import "bootstrap";
続いて、app/assets/javascripts/application.jsに以下の記述を加えます。
//= require bootstrap-sprockets
ここまでで、RailsアプリへのBootstrapの導入は完了です。
一応シェルのカレントディレクトリは作成したアプリケーション直下に戻しておきましょう。
簡単なアプリをscaffoldで作成
今回はnameという文字列属性を持ったuserというモデルをベースで、アプリを作成します。
$ rails g scaffold user name:string
作成し終わったら、忘れずにdbを初期化します。
$ rails db:migrate
これでアプリのベースが作成されています。試しに開発サーバを起動して表示を確認してみましょう。今回作成した
$ rails s
エラー無く起動したら、ブラウザでlocalhost:3000/usersを表示し、それっぽい画面が表示されていればここまではOKです。続いてBootstrapの確認に移ります。
Bootstrapの確認
今回作成したフォルダのapp/views/users/index.html.erbをエディタで開きます。最下部の方に
<%= link_to 'New User', new_user_path %>
こんな行があるのを
<%= link_to 'New User', new_user_path, class: "btn btn-lg btn-primary" %>
こんな感じに書き換えます。ここではリンクの属性クラスにBootstrap標準で用意されているボタン表示用のクラスを適用しています。
ここまで出来たらブラウザで先程の/users/をリロードしてみましょう。表示が青いボタンっぽくなっていれば、無事Bootstrapは導入されています。
Newフォームのモーダル化
ここまででベースは整ったので、やっとNewフォームのモーダル化に入ります。
新規作成フォーム呼び出し部分のAjax化
index.html.erb中から、新規作成フォーム(new)に遷移しているリンクをAjax化します。とは言ってもRails君は賢いので一つオプションを加えるだけです。
<%= link_to 'New User', new_user_path, remote: true, class: "btn btn-lg btn-primary" %>
ここで追加したのはremote: trueの部分です。このオプションを付け加えることで、リンクからの呼び出し先がnew.html.erbからnew.js.erbに変更されます。この状態でリンクをクリックしてもスカスカするだけで何も起きませんが、ログにはそれっぽい内容が出力されているはずです。
続いて呼び出し先であるnew.js.erbを新規作成します。
new.js.erbの新規作成
お使いのエディタでapp/views/users/配下にnew.js.erbという名前でファイルを作成します。内容は以下の通り。
$("#user-modal").html("<%= escape_javascript(render 'form') %>")
$("#user-modal").modal("show")
一行目はidがuser-modalとなっているindex.html.erb中のDivに対して、新規作成用のformをレンダリングしています。
二行目はレンダリングしたフォームに対してmodalで表示するように指示しています。
それでは、index.html.erbに表示用のDivを加えましょう。
表示用Divの挿入
先程remoteオプションを作成したリンクタグの下あたりに、以下を追加します。
<div id="user-modal" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true"></div>
このDivはidを先程のJavaScriptで指定していたuser-modalに、classをBootstrapのmodal表示で使用するmodal(+fade)、tabindexを-1で指定して背面に、roleをBootstrapで使用するダイアログ形式表示のdialogに、モーダル表示したときに周りが暗くなるようaria-hidden属性をtrueに設定しています。個々の細かい指定の意味はGoogle先生に聞けばすぐ出てくると思います。
表示フォームのBootstrap属性指定
Bootstrapでモーダルフォームの表示制御をするには、表示したい部分を指定のクラス指定したDivで囲ってやる必要があります。
今回表示したいのは入力フォームなので、自動生成された部分の上下を以下のDivでくくってやります。
<div class="modal-dialog">
<div class="modal-content">
...
</div>
</div>
ここまでで新規作成リンクをクリックすると入力フォームがモーダルで表示されていると思います。それでは、次はモーダルフォームからの実際の新規作成処理もAjax化していきます。
新規作成処理のAjax化
まずはapp/views/users/にある_form.html.erbを開きます。これが入力フォームの元です。
開いたら上部にあるformタグにremoteオプションを付け、新規作成処理がJavascript指定でコントローラーに流れるようにします。
<%= form_for(@user, remote: true) do |f| %>
次はコントローラー側の新規作成部分(create)で、Javascript指定で来たリクエストを処理出来るようにします。
def create
@user = User.new(user_params)
respond_to do |format|
if @user.save
format.html { redirect_to @user, notice: 'User was successfully created.' }
format.json { render :show, status: :created, location: @user }
format.js { @status = "success"}
else
format.html { render :new }
format.json { render json: @user.errors, status: :unprocessable_entity }
format.js { @status = "fail" }
end
end
end
全体で見るとこんな感じのメソッドになりますが、追加しているのはif/elseそれぞれの一番下にあるformat.jsのそれぞれ一行ずつです。
ここでは入力値から実際にDBにデータを作成し終わった後、成功ならインスタンス変数の@statusにsuccessを、失敗ならfailを代入しています。実際の作成処理はここでもう行われていますが、次にモーダルフォームを閉じつつ、作成されたデータによって一覧(index)をAjax的に更新します。
モーダルフォームの非表示と一覧表示の更新
上記のcreate処理の後に、Railsがcreate.js.erbを呼び出します。まずはこのファイルをいつものviews/users配下に作成します。
<% if @status == 'success' %>
$("tbody").append("<%= j(render("tr", user: @user)) %>");
<% elsif @status == 'fail' %>
alert('error!');
<% end %>
$("#user-modal").modal("hide");
ここでは先程のcreateメソッドで作成したインスタンス変数("@success")を元に、処理を切り分けています。
成功の場合は追加したuserデータを元にテーブルに行の追加を、失敗の場合はJSのアラートダイアログでエラーを表示しています。
その後一番最後の行で、モーダルダイアログを非表示にしています。
成功した場合の行追加用のテンプレートが必要になりますので、これをいつもの場所に作成します。
<tr>
<td><%= user.name %></td>
<td><%= link_to 'Show', user %></td>
<td><%= link_to 'Edit', edit_user_path(user) %></td>
<td><%= link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?'} %></td>
</tr>
これで全体が完成です。お疲れ様でした。
終わりに
今回はRails5で試しましたが、Rails4でもおそらく上記手順で変わりが無く作成出来ると思います。
復習してみて思いましたが、一つ一つは単純なものの手順が多いので初めてやる場合は結構な確率でどこか抜けたりして上手く行かなかったりするでしょうし、初心者の場合はお手上げになる可能性も割とあると思います。
将来の自分も含めて、このメモ書き記事が誰かの役に立てば幸いです。