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>
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 %>
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 %>
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
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| %>
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>
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>
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>
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>
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 %>
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
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 %>
And finally, we can see how our form will appear to our user:
Sources
- "form_for", API dock, Accessed July 5 2020
- "Action View Form Helpers", Rails Guides, Accessed July 5 2020
Top comments (0)