Develop the right mindset for Rails security
Avoid shipping vulnerable code by learning how to prevent security issues in your Rails applications.
Get the course for $99This week I want to tell you about the form_with Rails helper.
It was introduced in Rails 5.1, and the goal of this helper was to unify the form_for and the form_tag helpers.
form_for requires a model instance or a scope and puts the attributes into a hash, so usually when you needed a form in the past where you didn’t want to put them scoped into a hash, you used form_tag.
Since the introduction of form_with, we can use the same helper for both use cases and specify a model, a scope, or just a URL.
If you specify a model instance, it infers the URL and the scope. If it is a persisted one, it will generate an update form with the appropriate URL and HTTP method.
<%= form_with model: Blog.new do |form| %>
<%= form.text_field :title %>
<% end %>
# will produce:
<form action="/blogs" method="post">
...
<input type="text" name="blog[title]">
</form>
<%= form_with model: Blog.first do |form| %>
<%= form.text_field :title %>
<% end %>
# will produce:
<form action="/blogs/1" accept-charset="UTF-8" method="post">
<input type="hidden" name="_method" value="patch" autocomplete="off">
...
<input type="text" value="testing" name="blog[title]" id="blog_title">
</form>
If you set a scope it will put all fields into a hash under the scope and if you only set a URL, the fields won’t be put into a hash:
# Adding a scope prefixes the input field names:
<%= form_with scope: :blog, url: blogs_path do |form| %>
<%= form.text_field :title %>
<% end %>
# will produce:
<form action="/blogs" method="post">
<input type="text" name="blog[title]">
</form>
<%= form_with url: blogs_path do |form| %>
<%= form.text_field :title %>
<% end %>
# will produce:
<form action="/blogs" method="post">
<input type="text" name="title">
</form>
You can also mix the above options and use a model with a custom scope and/or URL.
A few important things to remember about form_with:
- if you call
form_withwithout a block, it generates just an opening form tag - it has a
localoption, which is false by default, meaning it generates form tags submitted as a remote form with Rails UJS. This local option doesn’t control turbo though in Rails 7, if you want to disable that, you need to set the data-turbo attribute to false.
That’s it for the week. Until next time!