はじめに:私が犯した罪と償い
社内研修の一環で、プチIoTシステムを開発しています。
そんな中、自分のエラーの解消に大先輩3人の60分を溶かしてしまったので、せめてもの償いのために、学んだことをメモしておきます。
おかげで、
- Railsのメソッド
form_with
の引数local: true
が何の役に立っているのか - アプリ作成時のネットワーク通信のこと
が痛いほどわかりました。
環境
Rails6.0.3, macOS Catalina 10.15.7
何のエラー出してたん?
supplierモデルに対する、新規登録(new/create)のフォームで、バリデーションが通らなかった時にエラーメッセージをビューで表示したい!
ただそれだけのことでした。しかし仕込んだそのエラーメッセージが表示されません!
うまくいっていなかったビューはこちら。1行目にご注目。
<%= form_with model: @supplier do |form| %>
<% if form.object.errors.any? %>
<div id="error_explanation">
<div class="alert alert-danger">
エラーが <%= form.object.errors.count %> 個あります。
</div>
<ul>
<% form.object.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="mb-3">
<%= form.label :会社名, for: 'company', class: 'col-sm-2 col-form-label' %>
<%= form.text_field :name, class: 'form-control', id: 'company', placeholder: '株式会社 喧嘩上等' %>
</div>
<div class="mb-3">
<%= form.label :メールアドレス, for: 'email', class: 'col-sm-2 col-form-label' %>
<%= form.text_field :email, class: 'form-control', id: 'email', placeholder: 'order@example.com' %>
</div>
<%= form.submit '送信', class: 'btn' %>
<% end %>
私は、Railsチュートリアル出身なのですが、そこではform_with
にlocal: true
という引数を渡していました。しかし、ネットでform_withの使い方を他で改めて調べてみたところ、この引数がなくても新規レコードの登録処理ができそうだったので、省いてみちゃえ!と出来心が全ての事の始まりでした。実際、これでデータベースまでの登録はきちんとできていて、なんだlocalパラメータいらないんじゃん。となっていました。
一方.....if form.object.errors.any?
を入れても、エラー表示は一切できませんでした。
ちなみにコントローラはこんな感じ。問題なさそう。
class SuppliersController < ApplicationController
def new
@supplier = Supplier.new
end
def create
@supplier = Supplier.new(supplier_params)
if @supplier.save
redirect_to suppliers_path
else
render 'new'
end
end
end
binding.irb
をビューやコントローラなど至る箇所に埋め込んでは、各変数に値が入っているかどうかをチェック。
form.object
には値もある。@supplier
にしても同じ。errors.any?
もtrueを返している。何故なんだ。タイポもなさそう。先輩たちの時間をどんどん溶かしてしまっています...。
解決:「クルクルしてない!」
「クルクルしてない!」
問題の原因が全くわからず途方にくれていた末に、ある先輩が上記のセリフを発せられました。
フォームの送信ボタンを教えても画面がリロードしないことを変に思ったようで、そこからフォームの動きが怪しいとなりました。そこでlocal: true
を追記したのです。
<%= form_with(model: @supplier, local: true) do |form| %>
以下略
学び1:local: true
とは?
form_withはRailsの比較的新しいメソッド。このメソッドではデフォルトがAjax通信で、非同期通信になってしまうのです。(これはRails 5.1〜6.0の仕様でRails 6.1からは同期通信がデフォルトに戻っているそうです。当記事はRails6.0.3です。→参考。@jnchito さん、ありがとうございます!)それはつまり、必要な箇所だけページが更新され、その他の箇所はそのままになるという事。だからエラーメッセージのHTML部分が加えられなかったのです。
ここにlocal: true
と引数を渡す事で、これが通常のHTTPリクエストになり、ページ全体が返ってきてページがリロードされ、エラーメッセージも表示されるようになります。
学び2:アプリの開発でネットワークをチェックする
その後、ブラウザの開発者ツールでネットワークを確認しました。
↑Typeがxhrになっとる。xhr=XMLHttpRequest。Ajaxのxの部分。つまりAjax通信になっているということ。
↑suppliersが下の方に来ています。これはつまり、他の要素がリロードされずにそのまま残っているということ。
↓一方、local: true
にしておく(=通常のHTTP通信にする)と、下のようなネットワーク動作になります。
suppliersがリストの一番先頭にいるのが分かるかと思います。
これは、ページ内の全ての要素が更新され、suppliersが一番最初に処理されたという事です。
「エンジニア中級者になるには、ネットワークのことを理解すること」と先輩。
最後に
エラーの解消の過程で、他にもbinding.irbの使い方、errorsやfull_messagesにどんなデータが格納されているのかなどいろんなことを勉強できました。
先輩方ありがとうございました!
宣伝ではないですが、周りがこんなエラーに親身に向き合ってくれる先輩ばかりです。
本当にFusicという会社でエンジニアキャリアを始められて幸せすぎる...。