The differences between forms in Rails
Published: 2020-03-05 21:08:11

*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 <strong>E</strong>mbedded <strong>R</strong>u<strong>B</strong>y (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 <code>form_for</code>

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

```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? <code>form_for</code> is taking in an instance of <code>@cat</code> and working with a [FormBuilder](https://api.rubyonrails.org/v6.0.2.1/classes/ActionView/Helpers/FormBuilder.html) <code>|f|</code> 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

```erb
<%= 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 %>
```
<code>form_tag</code> 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


```erb
<%= 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 <code>form_for</code> over <code>form_tag</code> 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

```erb
<%= 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 %>
```

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

## fields_for 

```erb
<%= 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 <code>Recipe</code> and <code>Ingredient</code> class. A <code>Recipe</code> has many <code>Ingredient</code>s and a <code>Ingredient</code> belongs so a <code>Recipe</code>. 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 <code>fields_for</code> is allowing us to do is create a nested form that would then send the <code>:ingredients</code> nested in our params like <code>params[:recipe][:ingredients_attributes]</code>

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

- [Creating a form with form_with](https://medium.com/@tinchorb/form-with-building-html-forms-in-rails-5-1-f30bd60ef52d)

- [Rails Guides: Form Helpers](https://guides.rubyonrails.org/form_helpers.html)

- [API Dock: form_with](https://apidock.com/rails/ActionView/Helpers/FormHelper/form_with)

- [API Dock: form_tags](https://apidock.com/rails/ActionView/Helpers/FormTagHelper/form_tag)

- [API Dock: form_for](https://apidock.com/rails/ActionView/Helpers/FormHelper/form_for)