DEV Community

Cover image for What's this form_for ?
Iona Brabender
Iona Brabender

Posted on • Edited on

What's this form_for ?

photo by @markusspiske

When working with Rails, there are a variety of tools available which can aid us in constructing forms, including plain HTML, and form helpers such as form_tag, and form_for. In this post, we'll take a look at the benefits of using these form helpers, and also examine some of the features of the most advanced of these options, form_for, and how it can be used to easily and effectively allow your users to input information.

Why form_for?

The most basic way to construct a form would be simply using raw HTML. While this is a completely viable option, it can be far more laborious than taking advantage of a form helper. With HTML, we have to pass the action and method attribute we want to use, as well as set up our own authenticity token. Here, we can see a basic HTML form:

html forms


<h3> Create a New Company </h3><br>

<form action="<%= companies_path %>" method="POST">
    <label>Name:</label>
    <input type="text" id="company_name" name="company[name]"><br><br>
    <label>CEO:</label>
    <input type="text" id="company_ceo" name="company[ceo]"><br><br>
    <label>Industry:</label>
    <input type="text" id="company_industry" name="company[industry_id]"><br><br>

    <input type="hidden" name="authenticity_token" id="authenticity_token" value="<%= form_authenticity_token %>">
    <input type="submit" value="Submit">
</form>
Enter fullscreen mode Exit fullscreen mode

While this will do the job, it can be messy, especially as our work becomes more complex. It also involves writing quite a lot of repetitive code. Form helpers such as form_tag and form_for can alleviate some of these problems for us.

form_tag

form_tag is the most basic form helper available to us. It automatically generates HTML code, and enables the user to submit data that will then be passed to the controller. It also creates the necessary authenticity token for us. Here we can see the same form we created above, this time using form_tag:

<%= form_tag companies_path(@company), method: "post" do %>
    <label>Name:</label>
    <%= text_field_tag :name, @company.name %><br><br>
    <label>CEO:</label>
    <%= text_field_tag :ceo, @company.ceo %><br><br>
    <label>Industry:</label>
    <%= text_field_tag :industry_id, @company.industry_id %><br><br>
    <%= submit_tag "Submit Post" %><br> 
<% end %>
Enter fullscreen mode Exit fullscreen mode

While this is a significant upgrade from the HTML form, using form_tag means that we must still manually pass our form to the route where the parameters will be submitted. Additionally, our form doesn't know its function, for instance whether it is supposed to create or make changes to a record, and we still have quite a lot of repetitive code. Therefore, form_tag can be helpful when we simply need to generate a HTML form, however isn't really suitable for creating or updating records, as we are doing here.

form_for

Generally, the best form option when you need to perform any sort of CRUD operation and interact with your database is form_for. form_for follows RESTful conventions, and can therefore make assumptions about routes, and what the form is trying to do. Here, we can see our form again, this time using form_for:

<h3> Create a New Company </h3><br>

<%= form_for @company do |c| %>
    <%= c.label :name %>
    <%= c.text_field :name %><br><br>
    <%= c.label :ceo %>
    <%= c.text_field :ceo %><br><br>
    <%= c.label :industry_id %>
    <%= c.text_field :industry_id %><br><br>
    <%= c.submit %>
<% end %>
Enter fullscreen mode Exit fullscreen mode

By using form_for, we can minimise how much code we have to write, and create a form more suited to our CRUD needs.

How to Use form_for

We can now take a look at how form_for works, as well as at some of its most useful features. In this example, we'll imagine that we're working with a database comprised of basic information about a variety of different companies in different fields and locations. We want to allow users to add their business to our database using a form.

CompaniesController

Before we start working on our form, we need to make sure everything is organised behind the scenes. After setting up our routes, we can write our new and create actions in our CompaniesController like so:

    def new
        @company = Company.new
    end

    def create
        @company = Company.new(company_params)
        if @company.save
            redirect_to @company
        else
            flash[:message] = @company.errors.full_messages
            render :new
        end
    end

    private

    def company_params
        params.require(:company).permit(:name, :ceo, :slogan, :number_of_employees, :currently_operating, :location_id, :industry_id)
    end

end

Enter fullscreen mode Exit fullscreen mode

Here, we're creating a new instance of a Company based on our user's input. We're also using strong params here (def company_params), meaning that our user is limited in terms of what information they can add to our database. We want to ensure that our database is protected, and that we're not just taking in any information that the user decides to give us. By requiring :company, we are stating that our params hash must contain a key called "company" in order to save the information in our database. By permitting other attributes, we are indicating what else we'll accept. We've also included an if statement in our create action. This means that we'll only try to redirect the user to the show page for their newly input company if it's been successfully created. If there are any problems, for instance caused by validation restrictions, they'll be redirected back to the new company page. Additionally, we've chosen to include a flash message here, which will alert the user to any problems that have arisen when they've tried to submit their form. We'll take a look in more detail at this later.

Setting up Your Form

We'll start off our form_for like so:

<%= form_for @company do |f| %>
Enter fullscreen mode Exit fullscreen mode

Here, our form takes an instance of a Company (@company), which it will then pass to the CompaniesController, where a new company will be created using the information provided by our user.

text_field

The first input we need from the user is the company name and the name of the company CEO. We can create a simple text_field, meaning the user is free to type in whatever company name they like. At this point, we haven't included any validations, so there are no restrictions, for instance on the length or uniqueness of input. We also have control over the text in our label. By including

html 'CEO:'

we're deciding exactly what the user will see. Alternatively, this specific text can be omitted, and the label will be generated for us.

    <%= f.label :name, 'Name:' %>
    <%= f.text_field :name %><br><br>

    <%= f.label :ceo, 'CEO:' %>
    <%= f.text_field :ceo %><br><br>
Enter fullscreen mode Exit fullscreen mode

collection_select

Our next two fields are ones that we'd prefer the user not to have complete freedom over. At this point, we only want our user to be able to enter industries and locations that already exist in our database. In order to do this, we can use collection_select. In our industry example, :industry_id represents the column in our companies table that this data will be linked to. Industry.all shows that we want our user to be able to see options from our industry table, :id indicates what information we want to take, and :name is what the user will see. We could, for instance, replace :name with :id, meaning that the user would have to select an option based on an integer representation of the :id, but this wouldn't be very user-friendly.

    <%= f.label :industry, 'Industry:' %>
    <%= f.collection_select :industry_id, Industry.all, :id, :name %><br><br>

    <%= f.label :location, 'Location:' %>
    <%= f.collection_select :location_id, Location.all, :id, :name %><br><br>
Enter fullscreen mode Exit fullscreen mode

number_field

We also want to include the number of employees working for the company. We want to ensure that our users only enter numbers, so we can set this up as a number_field. This will allow users to input integers (it won't be possible for them to enter text), and will also give them the option to increase or decrease that number using buttons in the form.

    <%= f.label :number_of_employees, 'Number of Employees:' %>
    <%= f.number_field :number_of_employees %><br><br>
Enter fullscreen mode Exit fullscreen mode

check_box

Finally, one of our fields, currently_operating, is a boolean. It wouldn't be very user-friendly to require users to input true or false, and we don't want to make things more complicating by having to convert their input to something our database understands. Therefore, we can use a check_box. When the user checks this box on our form, our form will understand their answer as "true", and will therefore save it accurately in our database. Alternatively, if the box is left unchecked, we'll understand this as false.

    <%= f.label :currently_operating, 'Currently Operating?' %>
    <%= f.check_box :currently_operating %><br><br>
Enter fullscreen mode Exit fullscreen mode

submit

We also need our user to be able to submit their form, in order for their information to be able to enter our database. We can do this simply by adding a submit button, and closing our form.

    <%= f.submit  %>
<% end %>
Enter fullscreen mode Exit fullscreen mode

flash[:message]

As mentioned above, we've also decided to include a flash message in our form. A flash message is very useful in giving the user information about any problems that have come up when they've tried to submit their form. For instance, we could require users to enter a name for their company before we allow it to be saved in our database. We would therefore include this validation in our Company model.

class Company < ApplicationRecord

    belongs_to :industry
    belongs_to :location

    validates :name, presence: true

end
Enter fullscreen mode Exit fullscreen mode

If the user then tries to submit a new company without adding a name, the form will be rejected, and they'll be shown the new form again with the error message " ["Name can't be blank"] " at the top. Flash messages are completely optional, and this step can be omitted, however it's very useful for the user to know what has led to their input being rejected.

Our Finalised Form

We can put together all of this code, and create our finalised form like so:


<h2><%= "Create a New Company" %></h2>

<%= flash[:message] %>
<%= form_for @company do |f| %>
    <%= f.label :name, 'Name:' %>
    <%= f.text_field :name %><br><br>

    <%= f.label :ceo, 'CEO:' %>
    <%= f.text_field :ceo %><br><br>

    <%= f.label :industry, 'Industry:' %>
    <%= f.collection_select :industry_id, Industry.all, :id, :name %><br><br>

    <%= f.label :location, 'Location:' %>
    <%= f.collection_select :location_id, Location.all, :id, :name %><br><br>

    <%= f.label :slogan, 'Slogan:' %>
    <%= f.text_field :slogan %><br><br>

    <%= f.label :number_of_employees, 'Number of Employees:' %>
    <%= f.number_field :number_of_employees %><br><br>

    <%= f.label :currently_operating, 'Currently Operating?' %>
    <%= f.check_box :currently_operating %><br><br>

    <%= f.submit  %>
<% end %>

Enter fullscreen mode Exit fullscreen mode

And finally, we can see how our form will appear to our user:

form_for

Sources

  1. "form_for", API dock, Accessed July 5 2020
  2. "Action View Form Helpers", Rails Guides, Accessed July 5 2020

Top comments (0)