18
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

Organization

form_withのlocal: trueって必要なん?これ何なん?(Ruby on Rails)

はじめに:私が犯した罪と償い

社内研修の一環で、プチIoTシステムを開発しています。
そんな中、自分のエラーの解消に大先輩3人の60分を溶かしてしまったので、せめてもの償いのために、学んだことをメモしておきます。

おかげで、

  1. Railsのメソッドform_withの引数local: trueが何の役に立っているのか
  2. アプリ作成時のネットワーク通信のこと

が痛いほどわかりました。

環境
Rails6.0.3, macOS Catalina 10.15.7

何のエラー出してたん?

supplierモデルに対する、新規登録(new/create)のフォームで、バリデーションが通らなかった時にエラーメッセージをビューで表示したい!
ただそれだけのことでした。しかし仕込んだそのエラーメッセージが表示されません!

うまくいっていなかったビューはこちら。1行目にご注目。

new.html.erb
<%= 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_withlocal: trueという引数を渡していました。しかし、ネットでform_withの使い方を他で改めて調べてみたところ、この引数がなくても新規レコードの登録処理ができそうだったので、省いてみちゃえ!と出来心が全ての事の始まりでした。実際、これでデータベースまでの登録はきちんとできていて、なんだlocalパラメータいらないんじゃん。となっていました。

一方.....if form.object.errors.any?を入れても、エラー表示は一切できませんでした。

ちなみにコントローラはこんな感じ。問題なさそう。

supplier.controller.rb
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を追記したのです。

new.html.erb
<%= 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:アプリの開発でネットワークをチェックする

その後、ブラウザの開発者ツールでネットワークを確認しました。
スクリーンショット 2021-02-25 15.06.16.png
↑Typeがxhrになっとる。xhr=XMLHttpRequest。Ajaxのxの部分。つまりAjax通信になっているということ。

スクリーンショット 2021-02-25 15.06.04.png
↑suppliersが下の方に来ています。これはつまり、他の要素がリロードされずにそのまま残っているということ。

↓一方、local: trueにしておく(=通常のHTTP通信にする)と、下のようなネットワーク動作になります。
suppliersがリストの一番先頭にいるのが分かるかと思います。
これは、ページ内の全ての要素が更新され、suppliersが一番最初に処理されたという事です。
スクリーンショット 2021-02-25 15.08.37.png

エンジニア中級者になるには、ネットワークのことを理解すること」と先輩。

最後に

エラーの解消の過程で、他にもbinding.irbの使い方、errorsやfull_messagesにどんなデータが格納されているのかなどいろんなことを勉強できました。

先輩方ありがとうございました!

@yuuu
@yukabeoka
@Junkins

宣伝ではないですが、周りがこんなエラーに親身に向き合ってくれる先輩ばかりです。
本当にFusicという会社でエンジニアキャリアを始められて幸せすぎる...。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
18
Help us understand the problem. What are the problem?