すでにメンバーの場合は

無料会員登録

GitHubアカウントで登録 Pikawakaが許可なくTwitterやFacebookに投稿することはありません。

登録がまだの方はこちらから

Pikawakaにログイン

GitHubアカウントでログイン Pikawakaが許可なくTwitterやFacebookに投稿することはありません。

Rails

【Rails】remote: trueでフォーム送信をAjax実装する方法とは?

ぴっかちゃん
ぴっかちゃん

form系のヘルパーメソッドには、:remoteオプションがあります。これをtrueに設定(remote: true)することで、 Ajaxを簡単に実装する事が出来ます。

Ajaxを実装すると、以下の動画のように画面遷移することなく、データを保存してページの一部だけ更新することが出来ます。

フォーム送信のAjax実装の完成図

Railsでは、フォーム系ヘルパーなどにremote: trueを設定することで、簡単に実装することが出来ます。

Ajaxとは、JavaScriptでサーバー側との通信を「非同期」で行い、通信結果によって「動的にページの一部だけ書き換える手法のこと」です。

そもそも「Ajaxって何だろう?」という方や「Ajaxの仕組み」について理解出来ていない方は、こちらの「Ajaxとは?初心者向けに仕組みを徹底解説!」を読んでからAjax実装に入りましょう。

フォーム送信をAjaxで実装する方法

この章では、remote: trueを使用した場合のAjaxの実装方法を知りたい人・仕組みを理解したい人に向けて、フォーム送信をAjaxに実装しながら1つ1つ解説します。

以下の動画のように、実装前(右側)は画面遷移して更新していましたが、Ajaxを実装(左側)すると、画面遷移なしで登録したメッセージを追加する事が出来ます。

(左側)Ajax実装済み / (右側) Ajax未実装
Ajaxを実装した場合としていない場合のアプリケーション

Ajaxを実装する流れは、以下の通りです。

  1. 前準備:アプリケーション作成する
  2. フォームヘルパーにremote: trueを設定する
  3. リクエストされるフォーマットで処理を分ける
  4. 「〇〇.js.erbファイル」を作成する
  5. jsファイルに更新処理を記述する

remote: trueを使った事がない人は、この機会に是非アプリケーションを作成しながら進めてみてください。アプリケーションを作成する事で、より一層理解を深める事が出来ます

前準備:アプリケーション作成する

Ajaxを実装する前に、メッセージを投稿することが出来る簡単なアプリケーションを用意します。まずは、以下のコードを実行してrails newsample_appアプリケーションを作成します。

ターミナル | データベースをMySQLを指定してアプリケーションを作成する
1
rails _5.2.1_ new sample_app -d mysql

次に、以下のコードのscaffoldでMessageモデルなど投稿メッセージを管理する為の雛形を作成します。

ターミナル | 投稿メッセージを管理する為の雛形を作成
1
2
3
cd sample_app # ディレクトリを移動
rails g scaffold Message body:string # scaffoldを実行
# rails g scaffold モデル名 カラム名:型

そして、データベースの作成とscaffoldの内容を元にして作成されたマイグレーションを実行します。

ターミナル | データベース作成とマイグレーションを実行
1
bundle exec rails db:create && bundle exec rails db:migrate 

ここまで実行したら、rails sでサーバーを起動させてlocalhost:3000/messagesにアクセスすると、以下の動画のようにメッセージの登録・一覧表示・編集など簡単な機能が一通り作成されています。

scaffoldで作成したアプリケーション

Ajaxはまだ実装していない状態なので、上記の動画のようにメッセージを登録したり、メッセージの一覧を表示する際に画面遷移しています。これにremote: trueを使ってAjaxを実装する事で、画面遷移なしで登録したメッセージを表示出来るようにします。

1.フォームヘルパーにremote: trueを設定する

以下の画像のように、一覧画面に入力フォームと登録したメッセージを表示させます。

一覧画面に入力フォームと登録したメッセージを表示させる

まずは、以下のコマンドでapp/views/messages/配下に_message.html.erbを作成し、コードを記述します。

コンソール | _message.html.erbファイルを作成する
1
touch app/views/messages/_message.html.erb
app/views/messages/_message.html.erb | 記述するコード
1
2
3
<li class="message"><%= message.body %></li>
<%# messageには、呼び出し側の@messagesの中身が1つ1つ渡される%>
<%# @messagesが全て渡されるまで、このテンプレートは繰り返し呼ばれる %>

上記の部分テンプレートは、この後に記述するindex.html.erb<%= render @messages %>で呼び出されます。

そして、messages_controller.rbのindexアクションindex.html.erbをそれぞれ以下のコードに書き換えます。

app/controllers/messages_controller.rb |indexアクションの内容を変更
1
2
3
4
5
6
class MessagesController < ApplicationController
  def index
    @messages = Message.all
    @message = Message.new # 追加
  end
end
app/views/messages/index.html.erb | 一覧画面にメッセージ登録フォームを追加
1
2
3
4
5
6
7
8
9
10
11
<h1>Messages</h1>

<div class="contents">
  <%= form_with(model: @message, class: 'js-form') do |f| %>
    <%= f.text_field :body %>
    <%= f.submit %>
  <% end %>
  <ul class="messages">
    <%= render @messages %> <%# _message.html.erbを呼び出す %>
  </ul>
</div>

index.html.erbでは、簡単に入力フォームを作成してくれるform_withのヘルパーメソッドを使用します。form_withは、デフォルトで非同期通信をするように設定されているので、remote: trueを記述する必要はありません。

以下のコンパイル後のコードでは、formタグにdata-remote="true"が追加されます。

HTML | コンパイル後のコード
1
2
3
<form  class="js-form" action="/messages" accept-charset="UTF-8" data-remote="true" method="post">
<!-- 省略 -->
</form>

localhost:3000/messagesにアクセスして検証をすると、以下のようにformタグにdata-remote="true"が追加されているのが分かります。

コンパイル後のコードを確認

これにより、フォームを送信する際にHTML形式ではなくJS形式で送信する事ができます。

リクエストされる形式を調べる

リクエストされる形式を調べるには、request.formatを実行します。

まずは、以下のようにMessagesControllerのcreateアクションにbinding.pryでブレークポイントを記述します。(gemのインストール方法は、こちらの記事を参考にしてください。)

app/controllers/messages_controller.rb | createアクションにbinding.pryを記述する
1
2
3
4
5
6
7
class MessagesController < ApplicationController
 def create
    binding.pry # 追加
    @message = Message.new(message_params)
    # 省略
  end
end

そして、以下の動画のようにフォーム送信すると、プログラムの実行が停止されPryが起動します。そこでrequest.fomatを実行するとリクストされた形式を調べる事が出来ます。

remote-trueを設定して送信されたリクエストされた形式を調べる

以下のように、request.formatの結果にapplication/javascriptとあるように、form_withで作成したフォームのリクエストがJS形式で送信されていることが分かります。

コンソール | リクエストされた形式がJSの場合
1
2
3
4
[1] pry(#<MessagesController>)> request.format
=> #<Mime::Type:0x00007f91a4ad6730 @hash=-969871837971611020, 
@string="text/javascript", @symbol=:js,
@synonyms=["application/javascript", "application/x-javascript"]>

一方で、コンパイル後のformタグにdata-remote="true"がない場合は、リクエストがHTML形式で送信されます。

form_withでは、以下のようにlocal: trueを追加することでdata-remote="true"を無くして、HTML形式でリクエスト送信することが出来ます。

index.html.erb | form_withでリクエストをHTML形式で送信する場合
1
2
<%= form_with(model: @message, class: 'js-form' local: true) do |f| %>
<% end %>

localhost:3000/messagesにアクセスして検証をすると、以下のようにformタグにdata-remote="true"が無くなっている事が分かります。

data-remoteがない場合のformタグ

そして、以下の動画のようにフォーム送信すると、プログラムの実行が停止されPryが起動するので、request.fomatを実行します。

先ほどはapplication/javascriptでしたが、今度はapplication/xhtml+xmlとあるように、以下の結果からリクエストされた形式がHTMLで送信されていることが分かります。

コンソール | リクエストされた形式がHTMLの場合
1
2
3
[1] pry(#<MessagesController>)> request.format
=> #<Mime::Type:0x00007f91a4ad6d70 @hash=-2181586491714069490, 
@string="text/html", @symbol=:html, @synonyms=["application/xhtml+xml"]>
ポイント

form_withでは、data-remote="true"がデフォルトなのでremote: trueは必要ありませんが、form_forform_tagでJS形式のリクストを送信する場合はremote: trueを設定する必要があるので注意しましょう!

form_forメソッドを使用する場合

form_forメソッドにremote: trueを設定しない場合は、以下のようにコンパイル後のコードにdata-remote="true"は存在しません。

form_forメソッドにremote: trueを設定しない場合
1
2
3
4
5
6
7
8
9
<%= form_for(@message) do |f| %>
  <%= f.text_field :body %>
  <%= f.submit %>
<% end %>

<!-- コンパイル後のコード-->
<form class="new_message" id="new_message" action="/messages" accept-charset="UTF-8" method="post">
<!-- 省略-->
</form>

しかし、form_forメソッドにremote: trueを設定した場合には、以下のようにコンパイル後のコードにdata-remote="true"が追加されます。

index.html.erb | form_forメソッドにremote:trueを設定した場合
1
2
3
4
5
6
7
8
9
<%= form_for(@message, remote: true) do |f| %>
  <%= f.text_field :body %>
  <%= f.submit %>
<% end %>

<!-- コンパイル後のコード-->
<form class="new_message" id="new_message" action="/messages" accept-charset="UTF-8" data-remote="true" method="post">
<!-- 省略-->
</form>

form_tagメソッドを使用する場合

form_tagメソッドを使用した場合も同様でremote: trueを設定しない場合は、以下のようにコンパイル後のコードにdata-remote="true"は存在しません。

form_tagメソッドにremote: trueを設定しない場合
1
2
3
4
5
6
7
8
9
<%= form_tag('/messages', method: :post) do %>
  <input type="text" name="message[body]">
  <input type="submit">
<% end %>

<!-- コンパイル後のコード-->
<form action="/messages" accept-charset="UTF-8" method="post"> 
 <!-- 省略-->
</form>

しかし、form_tagメソッドにremote: trueを設定した場合には、以下のようにコンパイル後のコードにdata-remote="true"が追加されます。

index.html.erb | form_tagメソッドにremote:trueを設定した場合
1
2
3
4
5
6
7
8
9
<%= form_tag('/messages', method: :post, remote: true) do %>
  <input type="text" name="message[body]">
  <input type="submit">
<% end %>

<!-- コンパイル後のコード-->
<form action="/messages" accept-charset="UTF-8" data-remote="true" method="post"> 
 <!-- 省略-->
</form>

2.リクエストされるフォーマットで処理を分ける

Railsのリクエスト送信〜レスポンスが返るまでの大枠の流れは、以下の通りです。

リクエストからレスポンスまでの大枠の流れ

  1. リクエストを送信する
  2. ルーティングで受け取ったリクエストに対応するコントローラのアクションを割り当てる
  3. 割り当てられたコントローラのアクションが実行される
  4. アクションの処理終了後、アクション名と同じビューファイル(views/コントローラー名/アクション名.リクエスト形式.erb)が呼び出される

上記の「4. アクション名と同じビューファイルが呼び出される」は、以下の画像のようにリクエストされる形式によって変わります。

リクエストの形式によって呼び出されるビューファイルの違い

JS形式でリクエストを送信した場合は、アクション名.js.erb、HTML形式でリクエストを送信した場合は、アクション名.html.erbが呼び出されます。

しかし、上記の画像のようにリクエストの形式ごとに呼び出されるビューファイルが違っても、その前に実行されるアクション(createアクション)は同じです。
その為、リクエストの形式ごとに処理を分ける場合は、「respond_toメソッド」を使います。

JS形式とHTML形式で処理を分ける場合

今回は、respond_toメソッドでリクエストされるフォーマット(HTML形式/JS形式)ごとに処理を分けていきます。
respond_toメソッドは、以下のコードのようにformat.形式で各形式の処理を記述することが出来ます。(詳細は、「respond_toメソッドの使い方」を参考にしてください。)

app/controllers/messages_controller.rb | respond_toメソッドでフォーマットごとに処理を分ける
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class MessagesController < ApplicationController
  def create
    @message = Message.new(message_params)

    respond_to do |format|
      if @message.save
        format.html { redirect_to @message } # showアクションを実行し、詳細ページを表示
        format.js  # create.js.erbが呼び出される
      else
        format.html { render :new } # new.html.erbを表示
        format.js { render :errors } # 一番最後に実装の解説あります
      end
    end
  end
end

上記の処理を簡単に解説すると、if文でデータベースにメッセージの保存が成功すればHTML形式の場合は、showアクションを実行します。JS形式の場合は、create.js.erbが呼び出されます。

そして、データベースの保存が失敗すればHTML形式の場合はnew.html.erbを表示します。JS形式の場合はerrors.js.erbが呼び出されます。(エラー実装のセクションで詳しく解説します。)

3.「〇〇.js.erbファイル」を作成する

メッセージの登録が成功すると、create.js.erbが呼び出されますが、scaffoldではこのファイルは作成されないので、以下のコマンドを実行してファイルを作成しておきます。

コンソール | create.js.erbファイル作成
1
touch app/views/messages/create.js.erb

create.js.erbでは、以下の3つのことが可能になります。

create.js.erbで出来る3つのことを解説

それでは、フォーム送信した際に本当にcreate.js.erbファイルは呼び出されるのか、ファイル内に記述したJavaScriptのコードは実行されるのか確かめます。

以下のコードをcreate.js.erbファイルに記述します。

app/views/messages/create.js.erb | console.logメソッドを記述する
1
console.log("create.js.erbファイルが呼び出されました!");

そして、以下の動画のようにフォーム送信して無事データが保存されるとcreate.js.erbファイルが呼び出されてconsole.log()の内容がコンソールに表示する事が出来ています。

jsファイルが呼び出されている様子

また、createアクションで定義したインスタンス変数@messageも、以下のようにcreate.js.erbファイルで使用する事が出来ます。

app/views/messages/create.js.erb | インスタンス変数を使用する
1
console.log("<%= @message.body %>");

コンソールでも以下のように表示されていますね。

インスタンス変数を使う事が出来ている様子

〇〇.js.erbファイルでは、JavaScriptとインスタンス変数(ERB)を使えるので、画面遷移なしで登録したメッセージの一部だけ追加する処理は簡単に記述する事が出来ます。

「〇〇.js.erbファイル」で出来ること
  1. JavaScriptを使って処理を記述できる
  2. ERBを使う事ができる
  3. インスタンス変数を使う事ができる

4. jsファイルに更新処理を記述する

最後に登録したメッセージを画面遷移なしで、以下の画像のように既に登録済みのメッセージのリストに追加して表示していきます。

画面遷移なしで、登録したメッセージを表示させる

それでは、以下のコードをcreate.js.erbに記述します。

app/views/messages/create.js.erb | 登録したメッセージを既存リストに追加する
1
$('.messages').append("<li class='message'><%= @message.body %></li>");

appendメソッドは、引数に指定した要素をある親要素の末尾に追加してくれるメソッドです。上記では、messageクラスが付与された親要素に引数の子要素を追加します。

messagesクラスの親要素は、以下の画像で既存メッセージを表示するul要素だと分かりますね。

既存メッセージのリストの要素を表示

このulの閉じタグの前に<li class='message'><%= @message.body %></li>が追加されます。@messageは、登録したメッセージの情報を保持しているので@message.bodyとすることで、メッセージの内容を表示させています。

アプリケーションを動かしてみると、以下のように画面遷移なしで登録したメッセージを表示することが出来ています。

登録したメッセージを既存リストに表示させている様子

しかし、このままでは入力したメッセージが登録後も残っているので、いちいち消してから次のメッセージを入力しなければなりません。

メッセージを登録したら入力フォームの内容をリセットする為に、以下のコードを追加しておきましょう。

app/views/messages/create.js.erb | 登録したメッセージを既存リストに追加する
1
2
$('.messages').append("<li class='message'><%= @message.body %></li>");
$('.js-form')[0].reset(); // 追加

js-formクラスは、form要素を指しています。resetメソッドを使うことで入力フォームの内容をリセットすることが出来ます。
resetメソッドについては、jQueryでresetメソッドを使う方法を参考にしてください。

登録したら入力した内容をリセットする

以上がremote: trueを使ったフォーム送信のAjax実装方法です。ここまでのAjax実装の全体の流れを以下の画像で一度整理してみましょう。

Ajax実装の全体の流れ

Ajax実装のポイント
  1. remote: trueを設定すると、リクストはJS形式で送信される
  2. コントローラのアクションの処理が終わると、アクション名.js.erbファイルが呼ばれる
  3. アクション名.js.erbファイルでは、JavaScript(jQuery)やERBでページの一部を更新する処理が記述出来る

今回は、form_withを使用したのでremote: trueは設定しませんでしたが、入力フォーム作成の際にform_forやform_tagを使用する場合は忘れないように気をつけてください。

部分テンプレートを呼び出して更新する

〇〇.js.erbファイルでは、部分テンプレートを呼び出して、ページの一部を更新する事が出来ます。

例えば、先ほど実装したcreate.js.erbファイルですが、メッセージ一覧に新しく登録したメッセージを追加する為、以下のように記述しています。

app/views/messages/create.js.erb | メッセージ一覧に登録したメッセージを追加する
1
$('.messages').append("<li class='message'><%= @message.body %></li>");

しかし、上記に記述するappendメソッドの引数の内容は、以下の_message.html.erbと同じです。

app/views/messages/_message.html.erb
1
<li class="message"><%= message.body %></li>

その為、appendメソッドの引数に_message.html.erb部分テンプレートを呼ぶことで、同じようにメッセージ一覧に新規メッセージを追加する事が出来ます。

部分テンプレートは、以下のように記述して呼び出します。

app/views/messages/create.js.erb | 部分テンプレートを呼び出してメッセージを追加する
1
$('.messages').append("<%= escape_javascript(render partial: 'messages/message', locals: { message: @message }) %>");

escape_javascriptは、改行やエスケープ処理をしてくれます。これはjというエイリアスを使用して、以下のように記述する事も出来ます。

app/views/messages/create.js.erb | escape_javascriptのエイリアスを使用する場合
1
$('.messages').append("<%= j(render partial: 'messages/message', locals: { message: @message }) %>");

appendメソッドの箇所を部分テンプレート呼び出しに変更しても、以下のようにメッセージ一覧に登録したメッセージを追加する事が出来ています。

部分テンプレートを使ってメッセージリストに追加した場合の挙動確認

このように、ページの一部を更新する際に部分テンプレートはよく使われるので覚えておきましょう!

非同期通信のエラー処理を実装する

メッセージの登録に失敗した場合は、以下の動画のように画面遷移なしでエラーメッセージを表示する事が出来ます。(メッセージが空の場合は、保存出来ないようにvalidationを追加しています。)

JS形式でリクエスト送信した場合のエラー処理

基本的にエラー処理はhtml形式でrenderメソッドを使用してrenderingすることが多いのですが、ここでは非同期通信のJS形式でエラー処理を実装してみましょう!

それでは、以下の手順でエラー処理を実装していきます。

  1. validationを追加する
  2. エラーメッセージの表示箇所を追加する
  3. 「errors.js.erbファイル」を作成する
  4. 更新処理を記述する

1.validationを追加する

まずは、メッセージが空で送信された場合に保存出来ないようにvalidationを追加します。
以下のコードをmessage.rbのMessageモデルに記述します。

app/models/message.rb | bodyカラムにvalidation追加する
1
2
3
4
class Message < ApplicationRecord
  # validates :カラム名, presence: true
  validates :body, presence: true
end

メッセージの内容を保存するカラムは、bodyなのでvalidates :bodyとします。
そして、指定したbodyカラムの「値が空ではない」ことをチェックしてくれるpresenceヘルパーを使用します。

2.エラーメッセージの表示箇所を追加する

次に、エラーメッセージを表示する箇所を追加します。
以下のように、index.html.erb<div class="js-message-errors"></div>を追加します。

app/views/messages/index.html.erb | エラーメッセージの表示箇所を追加する
1
2
3
4
5
6
7
8
9
10
11
<h1>Messages</h1>

<div class='contents'>
  <div class="js-message-errors"></div> <!-- 追加するコード -->

  <%= form_with(model: @message, class: 'js-form') do |f| %>
    <%= f.text_field :body %>
    <%= f.submit %>
  <% end %>
  <!-- 省略 -->
</div>

クラス名はjs-message-errorsとなっていますが、このクラスのdiv要素に後ほどJavaScriptで処理を追加することで、エラーメッセージを表示させる事が出来ます。

つまり、上記に追加したコードはJavaScriptを使って操作する為の目印となります。

3.「errors.js.erbファイル」を作成する

以下のmessages_controller.rbで、メッセージの保存が失敗したときの処理をもう一度確認します。メッセージの保存が失敗すると、以下のelse以降の処理が実行されます。

app/controllers/messages_controller.rb | 保存が失敗したときの処理を確認する
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class MessagesController < ApplicationController
  def create
    @message = Message.new(message_params)
    respond_to do |format|
      if @message.save
        format.html { redirect_to @message }
        format.js
      else
        # 保存が失敗した時に実行される処理
        format.html { render :new } 
        format.js { render :errors } # errors.js.erbが呼び出される
      end
    end
  end
end

また、リクエストはJS形式で送信されるので、format.js { render :errors }が実行されてerrors.js.erbが呼び出されます。

このerrors.js.erbは、app/views/messages/のファイルが呼び出されるので、以下のコマンドでファイルを作成します。

ターミナル | ファイルを作成する
1
touch  app/views/messages/errors.js.erb

4.更新処理を記述する

メッセージの保存が失敗した場合エラーメッセージが表示されるように、errors.js.erbに以下のコードを記述します。

app/views/messages/errors.js.erb | メッセージの保存が失敗した場合の処理を記述する
1
2
3
4
5
6
$('.js-message-errors').replaceWith(
  "<%= j(
    render 'layouts/validation_errors',
    instance: @message
  ) %>"
);

上記のコードは、index.html.erbに追加した.js-message-errorsの要素をreplaceWith()の引数で呼び出している部分テンプレートの内容に置き換えています。

部分テンプレートには、@messegeinstanceとして使えるように渡します。

部分テンプレート作成

この内容をもう少し理解する為に、以下のコマンドを実行して呼び出す部分テンプレート(_validation_errors.html.erb)を先に作成します。

ターミナル | ファイルを作成する
1
touch  app/views/layouts/_validation_errors.html.erb

次に、_validation_errors.html.erbにbinding.pryを記述して以下の動画ように空のメッセージを送信してみます。プログラム実行が停止したところでinstanceを調べると、空で送信したメッセージの情報(@message)が格納されています。

app/views/layouts/_validation_errors.html.erb | instanceの中身を確認する為binding.pryを記述する
1
<% binding.pry %>

部分テンプレートの呼び出しを確認する

また、データの保存が失敗すればエラー情報が追加されるので、上記のようにerrors.full_messagesを使って詳細なエラーメッセージを取得する事が出来ます。

したがって_validation_errors.html.erbに、以下のコードを記述することでinstanceにエラーメッセージが存在すれば、そのエラーメッセージを表示させる事が出来ます。

app/views/layouts/_validation_errors.html.erb | エラーメッセージがあれば表示させる
1
2
3
4
5
6
7
8
9
10
11
<!--エラーメッセージが存在すれば処理を実行する-->
<% if instance.errors.full_messages.present? %>
  <div class="validates js-message-errors">
    <ul>
      <!--エラーメッセージを全て表示させる-->
      <% instance.errors.full_messages.each do |msg| %>
        <li style="color: #bb4a4a;"><%= msg %></li>
      <% end %>
    </ul>
  </div>
<% end %>

ここまで実装した後に空メッセージを送信すると、以下の動画のように一覧画面にエラーメッセージが表示されます。検証部分に注目してみてください。

空メッセージ送信後エラーメッセージの表示を検証画面で確認する

元々あった.js-message-errorsの要素がreplaceWith()の引数に渡した部分テンプレートの内容に置き換わっていますね。

しかし、以下のように正常にメッセージを入力して送信しても、前に送信した際に表示されたエラーメッセージがそのまま残ってしまっています。

前処理のエラーメッセージが残っている状態

正常にメッセージを入力して送信した場合は、このエラーメッセージは表示させたくないので以下のようにcreate.js.erbempty().js-message-errors内の要素を空にします。

app/views/messages/create.js.erb | empty()で.js-message-errors内の要素を空にする
1
2
3
4
5
6
7
8
$('.messages').append(
  "<%= escape_javascript(
    render partial: 'messages/message',
    locals: { message: @message }
  )%>"
);
$('.js-form')[0].reset();
$('.js-message-errors').empty(); // 追加

エラー表示された後に正常に処理を行う場合

これでエラーメッセージが表示された後に、正常にメッセージ入力して送信してもエラーメッセージが表示されたままということもなくなります。

エラー処理の流れを整理する

ここまでのエラー処理の流れを一度整理しましょう。メッセージを空で送信した場合は以下の流れでエラーメッセージが表示されます。

空メッセージを送信した場合の全体の流れ

.js-message-errorsの要素を部分テンプレートの内容に置き換えて、エラーメッセージを表示している事が分かりますね。そして、以下の画像で置き換えられた部分テンプレートの内容を良くみると、js-message-errorsのクラス名がついたdiv要素があります。

部分テンプレートの内容を確認する

これは、部分テンプレート内で定義されています。以下のようにdiv要素にjs-message-errorsのクラスを付与しています。

app/views/layouts/_validation_errors.html.erb | 置き換えられる部分テンプレートのコードを確認する
1
2
3
4
5
<% if instance.errors.full_messages.present? %>
  <!-- 以下のdiv要素にjs-message-errorsのクラスを付与する -->
  <div class="validates js-message-errors">
    <ul>
    <!--省略-->

上記の部分テンプレートにこのクラスを付与していなければ、連続して空のメッセージが送信された場合は上手くエラーメッセージを表示する事が出来ません

なぜなら、1回目に空のメッセージを送信した時点で、replaceWith()によって.js-message-errorsは部分テンプレートに置き換えられるので、2回目以降に空メッセージを送信しても部分テンプレートに置き換えるための.js-message-errorsが存在しないからです。

js-message-errorsクラスを持つ要素がない場合

そのため、部分テンプレート内に.js-message-errorsのdiv要素を存在させる事で、2回目以降も空メッセージを送信してもエラーメッセージを表示させる事が出来ます。

今回は、remote: trueを使ってAjaxを実装しましたが、処理が複雑な場合はremote: trueだと実装が難しいので、JavaScriptで実装します。こちらの実装や処理の流れを知りたい方は、「Ajaxチュートリアル(Rails + jQuery)~処理の流れを理解しよう!」を参考に実装に挑戦してみてください。

この記事のまとめ

  • フォーム系ヘルパーにremote: trueを使うと、JS形式でリクエストを送信する事が出来る
  • JS形式でリクエストが送信された場合は、アクション名.js.erbファイルが呼び出される
  • アクション名.js.erbファイルは、ERBも使えるので部分テンプレート呼び出しやインスタンス変数を使ってページを一部更新する事が出来る!

1

わかった!