The differences between forms in Rails

austinoso

orio

Posted on March 5, 2020

The differences between forms in Rails

Note: This blog is more intended to show the differences between the different form helpers in Rails and not so much to explain how they work and how to use them

You might be new to Rails hearing about form_for, form_with, for_tags, and fields_for for the first time thinking, "What's with all these forms and fields?" and "When should I use a specific one?" All of these are form helper methods. We can take advantage of these in Rails with Embedded RuBy (erb) to create forms that render HTML. You might say? "Why not just build out a form in HTML? Using erb seems a lot more complicated." Well, to put in plain and simple. It just makes it easier, at least once you get the hang of it.

Form Helpers vs HTML

Using form_for

<%= form_for(@cat) do |f| %>
  <%= f.label :name %>
  <%= f.text_field :name %>
  <%= f.label :color %>
  <%= f.text_field :color %>
  <%= f.submit %>
<% end %>

vs HTML

<form accept-charset="UTF-8" action="/cats" method="post">
  <label for="cat_name">Name</label>
  <input id="cat_name" name="cat[name]" type="text" />
  <label for="cat_color">Color</label>
  <input id="cat_color" name="cat[color]" type="text" />
  <input name="commit" type="submit" value="Create" />
</form>

Let's compare the two forms. On the top we're creating a form using form_for in erb. Below we have what that bit of code creates in HTML/what we would have had to write in HTML. Except that in the form helper version, we didn't have to set our action path nor what method we wanted to use. Rails did that for us.

So what exactly is going on? form_for is taking in an instance of @cat and working with a FormBuilder |f| to create fields for that model. Assuming we had a create method in our CatsController, clicking submit in our above example would POST a @cat to the "/cats" route adding a new row to our database with the user set attributes.

What's the difference between all the Form Helpers?

Ok, I'm hoping by this point I've convinced you to start using form helpers instead of writing out your forms in HTML. But now I hear you asking, "What's the difference between form_for and form_with?" or "What even is fields_for?" All will be explained going forward.

Note: form_tag and form_for are being depreciated in replace of form_with. However, at the time of writing this, both for_tags and form_for still work so I will be including them.

form_tags

<%= form_tag("/cats") do %>
  <%= label_tag('cat[name]', "Name") %>
  <%= text_field_tag('cat[name]') %>

  <%= label_tag('cat[color]', "Color") %>
  <%= text_field_tag('cat[color]') %>

  <%= submit_tag "Create Cat" %>
<% end %>

form_tag is probably one of the more simpler and versatile form helpers. It builds us a simple form in HTML using the path we pass it. In this example, our action is the cats_path and it's setting our method to POST for us. Submitting this form (assuming we had a cats#create route and controller) would POST a new cat to our database.

form_for

<%= form_for(@cat) do |f| %>
  <%= f.label :name %>
  <%= f.text_field :name %>
  <%= f.label :color %>
  <%= f.text_field :color %>
  <%= f.submit %>
<% end %>

Now this is where that Rails magic starts. form_for works with an
instance of a class and you'll be able to create and update it's attributes. Plus if the instance you are referring to in the form already has set data. That data will populate your form, and Rails is smart enough to know it's an existing entry in the database and submit this form as an update.

When should you use form_for over form_tag and vice versa?

form_tag just creates a form in HTML. form_for on the other hand is great for working with data that connects with a model. A good example of when to use form_for is when we were working with out @cats model. We could easily call a specific cat.

form_with

<%= form_with url: cats_path do |f| %>
<%# You could also pass an instance such as @cat %>
  <%= f.label :name %>
  <%= f.text_field :name %>
  <%= f.label :color %>
  <%= f.text_field :color %>
  <%= f.submit %>
<% end %>

form_with is, at least the way I see it. The bridge between form_for and form_tags. It's meant to unify the two and replace them.

fields_for

<%= form_for @recipe do |f| %>
    <div>
        <%= f.label :title %>
        <%= f.text_field :title %>
    </div>

    <%= f.fields_for :ingredients do |ing| %>
        <p>Add Ingredient</p>
        <div>
            <%= ing.label :name %>
            <%= ing.text_field :name %>
        </div>
        <div>
            <%= ing.label :quantity %>
            <%= ing.text_field :quantity %>
        </div>
    <% end %>

    <%= f.submit %>

<% end %>

In this example, let's say a we have Recipe and Ingredient class. A Recipe has many Ingredients and a Ingredient belongs so a Recipe. When we create a recipe, wouldn't it make more sense to add ingredients in the same form we're creating the recipe itself? That's were nested forms comes in. Since this isn't entirely meant to be a how-to blog and more so to point you in the right direction. I won't be going too deep into how to create a nested form.

What fields_for is allowing us to do is create a nested form that would then send the :ingredients nested in our params like params[:recipe][:ingredients_attributes]

Hopefully after reading this you might be able to understand when to use the different form helpers in Rails. Again this post wasn't really meant to include how to use create forms.

References and further reading

πŸ’– πŸ’ͺ πŸ™… 🚩
austinoso
orio

Posted on March 5, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related