In the future, form_tag
and form_for
will not be used anymore and they will be replaced by form_with
. If you want to know more about form_with
, you can take a look at DHH’s proposal , check the pull request in which it was implemented, check the API documentation and try to create a project and use it.
Or, continue reading this article in which I explain the difference between form_tag
, form_for
and form_with
with examples.
A syntax used for all
Previously when you wanted to create a form, but you didn’t have a basic model for it, you would use form_tag
.
1 2 3 4 5 | <%= form_tag users_path do %> <%= text_field_tag :email %> <%= submit_tag %> <% end %> |
When you have a specific model, you will use form_for
.
1 2 3 4 5 | <%= form_for @user do |form| %> <%= form.text_field :email %> <%= form.submit %> <% end %> |
As you can see, we use form builder field helpers with form_for
but we don’t do that with form_tag
. Therefore, the syntax for each of these forms is different.
This situation will not happen with form_with
, because we always use the form builder.
form_with
has no model:
1 2 3 4 5 | <%= form_with url: users_path do |form| %> <%= form.text_field :email %> <%= form.submit %> <% end %> |
form_with
with model:
1 2 3 4 5 | <%= form_with model: @user do |form| %> <%= form.text_field :email %> <%= form.submit %> <% end %> |
When passing the model
parameters, the scope
and url
are automatically taken from it. This is similar to form_for
.
Automatic id and class are gone
form_tag
and form_for
automatically generate id for fields in the form. In addition, form_for
also creates the id for the form.
1 2 3 4 | <%= form_for User.new do |form| %> <%= form.text_field :email %> <% end %> |
Result:
1 2 3 4 5 | <form class="new_user" id="new_user" action="/users" ...> ... <input type="text" name="user[email]" id="user_email" /> </form> |
With form_with
, you must specify all your ids and classes manually:
1 2 3 4 5 | <%= form_with model: @user do |form| %> <%= form.text_field :name %> <%= form.text_field :email, id: :email, class: :email %> <% end %> |
Result:
1 2 3 4 5 | <form action="/users" ...> ... <input type="text" name="user[name]" /> <input id="email" class="email" type="text" name="user[email]" /> </form> |
Don’t forget to assign the id to the fields in the form if you want to make the label work.
1 2 3 4 5 | <%= form_with model: @user do |form| %> <%= form.label :name %> <%= form.text_field :name, id: :user_name %> <% end %> |
Update:
form_with
will automatically generate ids from Rails 5.2, so you don’t need to manually assign them anymore.
Form id and class attributes are no longer included
Before:
1 2 3 | <%= form_for @user, html: { id: :custom_id, class: :custom_class } do |form| %> <% end %> |
With form_with
:
1 2 3 | <%= form_with model: @user, id: :custom_id, class: :custom_class do |form| %> <% end %> |
Form fields do not have to correspond to model properties
Before:
1 2 3 4 5 | <%= form_for @user do |form| %> <%= form.text_field :email %> <%= check_box_tag :send_welcome_email %> <% end %> |
With form_with
:
1 2 3 4 5 6 | <%= form_with model: @user, local: true do |form| %> <%= form.text_field :email %> <%= form.check_box :send_welcome_email %> <%= form.submit %> <% end %> |
Note that send_welcome_email
will be restricted to the user
in the controller
parameters in the following example:
1 2 | params[:user][:send_welcome_email] |
Therefore, I would probably still use check_box_tag
instead of form.check_box
if I need to.
All forms are remote by default
This change is the most interesting to me. All forms created by form_with
will be sent by XHR (Ajax) request by default. No need to declare remote: true
as you have to do with form_tag
and form_for
.
I like this feature, as it sends a message. Thanks to Turbolinks, (almost) all GET requests are XHR requests by default. And thanks to form_with
, this can also become a reality for forms. You only need to do a little bit of work (create a JavaScript feedback form) and you won’t need to reload the page.
However, if you want to turn off remote forms then you can do so with local: true
.
1 2 3 | <%= form_with model: @user, local: true %> <% end %> |
Please use form_with from now on
I won’t cover everything in this article, but it’s enough to get you started with form_with so you don’t have to use form_tag and form_for anymore (they will be deleted anyway). Also, read the documentation for form_with .
Source: Patrik Bóna