form_with
をlocal: true
なしで使っても、通常エラーメッセージ部分は非同期処理になりません。
非同期で表示してみたので方法をまとめます。
FormのView
form_with
にlocal: true
を付けないと、非同期で通信が行われます。
app/views/tests/new.html.erb
<%= form_with model: @test do |f| %>
<%= f.text_field :name %>
<%= render 'shared/err_msg' %>
<%= f.submit %>
<% end %>
app/views/shared/err_msg.html.erb
<div id='err_container' style='display: none'>
<ul id='err_ul'>
<!-- ここにエラーメッセージが入る -->
</ul>
</div>
Controller
@test.save
が失敗した時、普通ならrender :new
でnewアクション用のviewファイルを表示するところですが、ここではapp/views/shared/err_msg.js.erb
に記述したJavaScriptを実行するようにしています。
app/controller/tests_controller.rb
def index
@test = Test.new
end
def create
@test = Test.new(test_params)
respond_to do |format|
if @test.save
format.js { redirect_to tests_url, notice: '作成しました' }
else
@object = @test
format.js { render 'shared/error', status: :unprocessable_entity } # ブラウザにstatus_code 422を返す
end
end
end
private
def test_params
params.require(:test).permit(:name)
end
エラーメッセージを表示するjs
エラーメッセージ全体のスタイルをdisplay: none
→display: block
に変更し、エラーメッセージをli
でラップして表示しています。
app/views/shared/err_msg.js.erb
let err_container = document.getElementById('err_container');
err_container.style.display = 'block';
let err_ul = document.getElementById('err_ul');
<% @object.errors.full_messages.each do |msg| %>
err_ul.insertAdjacentHTML('afterbegin', '<li><%= msg %></li>');
<% end %>