Using form_with vs form_for vs form_tag

source code form.jpg

Prior to Rails 5.1, Rails provided 2 form helpers, form_for and form_tag. You use form_for with a model and form_tag for custom URLs. Both generate HTML for a form. There are only a few minor differences so Rails 5.1 combined the two. You should now be using form_with.

form_with can do what the two old methods can do. When you pass a model, it acts the same as form_for. For example, if you have a Message model,

<%= form_with model: Message.new do |form| %>
  <%= form.text_field :subject %>
<% end %>

would generate

<form action="/messages" method="post" data-remote="true">
  <input type="text" name="subject">
</form>

If you have an existing model, the form will have the values filled in and the action would contain the ID i.e. it would go to the show action on the controller.

# controller
# @message = Message.first 

<%= form_with model: @message do |form| %>
  <%= form.text_field :subject %>
<% end %>

The form is similar to the one above. Because @message is an existing object, the generated form is different.

<form action="/messages/1" method="post" data-remote="true">
  <input type="hidden" name="_method" value="patch">
  <input type="text" name="message[subject]" value="<the subject of the message>">
</form>

If you don't pass a model, form_with behaves like form_tag.

<%= form_with url: messages_path do |form| %>
  <%= form.text_field :subject %>
<% end %>

would generate

<form action="/messages" method="post" data-remote="true">
  <input type="text" name="subject">
</form>

Options

form_with submits are remote by default (data-remote="true"). You can change this by using local: true. This is the opposite from the old methods where the default is local and you have to specify remote: true to make it remote.

<%= form_with(model: Message.new, local:true) do |form| %>
  <%= form.text_field :subject %>
<% end %>

You can also specify options available with form_for and for_tag like id, class, html, and data.

<%= form_with(model: Message.new, data: { behavior: "autosave" }, html: { name: "go" }) do |form|
  <%= form.text_field :subject %>
<% end %>
Ruby on Rails

Related posts

Comments

Subscribe to our Blog