form_withの使い方

form_withの使い方


[コントローラにモデルのインスタンスが定義されている場合でデータを保存する場合]
ex)

[app/controllers/boards_controller.rb]

def new
    @board = Board.new
end

def create
  @board = current_user.boards.new(board_params)
    if @board.save
      redirect_to boards_url, success: (t '.success')
  else
      flash.now[:danger] = (t '.fail')
      render :new
  end
end

def edit
  @board = current_user.boards.find(params[:id])
end

def update
  @board = current_user.boards.find(params[:id])
  if @board.update(board_params)
      redirect_to @board, success: (t '.success')
  else
      flash.now[:danger] = (t '.fail')
      render 'edit'
  end
end
[app/views/boards/new.html.erb]

<% content_for(:title, (t '.title')) %>
<div class="container">
  <div class="row">
    <div class="col-lg-8 offset-lg-2">
      <h1><%= (t '.title') %></h1>
      <%= render partial: 'form', locals: { board: @board } %>
    </div>
  </div>
</div>
[app/views/boards/edit.html.erb]

<% content_for(:title, @board.title) %>
<div class="container">
  <div class="row">
    <div class="col-lg-8 offset-lg-2">
      <h1><%= (t '.title') %></h1>
      <%= render partial: 'form', locals: { board: @board } %>
    </div>
  </div>
</div>
[app/views/boards/_form.html.erb]

<%= form_with model: board, local: true do |f| %>
    <%= render 'shared/error_messages', { object: f.object } %>
    <div class="form-group">
      <%= f.label :title %>
      <%= f.text_field :title, class: "form-control" %>
    </div>
    <div class="form-group">
      <%= f.label :body %>
      <%= f.text_area :body, class: "form-control", rows: "10" %>
    </div>
    <div class="form-group">
      <%= f.label :image %>
      <%= f.file_field :image, onchange: 'previewImage()', class: 'form-control mb-3', accept: 'image/*' %>
      <%= image_tag board.image.url, id: 'preview', size: '300x200' %>
    </div>
    <%= f.submit class: "btn btn-primary" %>
<% end %>


上記のような場合は、
[form_with model: コントローラで取得しているインスタンス名]と記載する事で、以下のようになる。
・[new.html.erb]よりformファイルが読み込まれる場合は、newアクションで取得しているインスタンスが[@board = Board.new]となっており、 [newメソッド]がある事により、railsが自動で予測してフォーム情報がcreateアクションにいくパスを自動生成する。
・[edit.html.erb]よりformファイルが読み込まれる場合は、editアクションで取得しているインスタンスが[@board = current_user.boards.find(params[:id])]となっており、 [findメソッド]がある事により、railsが自動で予測してフォーム情報がupdateアクションにいくパスを自動生成する。
※データの保存可否については、form_withに渡しているインスタンス変数がUser.newとなっているので、railsが自動でUserモデルを指定している。


[自動生成されるパスとは違う場所にフォーム情報を送る場合でデータを保存する場合]
ex)

[app/views/comments/_form.html.erb]

<%= form_with model: [board, comment], local: true do |f| %>
    <%= render 'shared/error_messages', object: f.object %>
    <div class="form-group">
      <%= f.label :body %>
      <%= f.text_area :body, class: 'form-control' %>
    </div>
    <%= f.submit (t 'defaults.post'), class: 'btn btn-primary' %>
<% end %>
[app/controllers/comments_controller.rb]

def create
    comment = current_user.comments.new(comment_params)
    if comment.save
      redirect_to board_url(comment.board_id), success: (t '.success')
    else
      redirect_to board_url(comment.board_id), danger: (t '.fail')
    end
end

[app/views/comments/_form.html.erb]を以下のように記載してしまうと、本来ならデータの行き先が、ルーティングをネスト化している関係で(/boards/:board_id/comments)に行かなくてはいけないが、comments_pathに行ってしまうので、url:[board, comment]と記載することでデータの行き先のパスを( /boards/:board_id/comments)に指定。

[app/views/comments/_form.html.erb]

<%= form_with model: comment, local: true do |f| %>

上記のurlでフォーム情報の行き先のパスを指定するだけだとフォームのデータを保存できないので、データを保存する先のmodelを特定するために[model:(コントローラで取得しているインスタンス変数名)を記載しなければならない。(データを保存するためには、モデルを特定しなければいけない。)
主にこのパターンは、ルーティングをネスト化している場合などに使う。


[フォームの情報を保存しない場合]

[app/views ファイル]

<%= form_with url: パス do |f| %>


※ポイント
フォームの情報を保存する為には、form_withでcreateアクションなどへのパスだけでなく[model: モデルのインスタンス]で保存するモデルを特定しなければいけない。

参考記事:

【Rails】form_withの使い方を徹底解説! | Pikawaka - ピカ1わかりやすいプログラミング用語サイト