Forms for New Instances in Rails
By: Charlie Knight
Tags: Rails, Collection Select, Forms

Recently I’ve been spending a fair amount of time in this area of rails, and much of that time was spent learning as opposed to executing; so I felt imparting some of my newly found knowledge could save someone else a few hours as they slog through the same experience. (Looking at you, Bootcampers).

Forms are just that, a form that a user can fill out on an application. Typically when we think about forms we think a doctor’s office or a survey but we are inputting information into forms constantly. While I’m writing this article I am putting data into a text_area variable. Filling out the title was most likely a text_field variable, which is very similar to area except for shorter sentences like names, mantras or titles. Below is a look at some of the different variables in forms and how they are represented on screen.

To start a form, use form_for in one of your views and tie it to a model.

text_field and text_area.

The above are examples of where a user inputs data manually, but what if we’d like to choose from a list? That’s where collection_select and collection_check_boxes come in handy. These can be a little bit trickier but are integral to a good experience on a website.

In this example I’m using something from my personal page. I am the user , and repos denote my GitHub repositories (kind of a bad example) but let’s say I wanted to add a repo to an instance of a user . To break it down:

:repos are part of the has_many relationship for a the user and we simply have to permit it in our strong params. Code is as follows:

private def user_params

params.require(:user).permit(:name, :repos)

end end

For this particular example we are only allowing one even though it is a one-to-many relationship.The next helper method allows us to fix that, and choose from many.

Repo.all are the choices we can choose from. Obviously we want a dropdown menu of all of the possible repos, so we call .all on the Repo class. Note that best practice says to set @repos = Repo.all and call @repos on the form.

:id and :url are attributes from the Repo class, :url is in the display position being the furthest to the right and :id is in the value position. It looks like this on the web application:

Repos can now be set from choices on a dropdown menu.

Similar to collection_select we can do collection_check_boxes which allows for multiple selections as opposed to one. This is a much better choice when you have a has_many relationship. Note that this can get a little bit tougher when you’re doing a many-to-many relationship. The code is essentially the same in the form_for . The major changes are in the model/controller. Below is the form.

<%=form_for @user do |f| %>

<%=f.label "Name"%>

<%=f.text_field :name%><br>

<%=f.label "Repos"%><br>

<%=f.collection_check_boxes :repo_id, Repo.all, :id, :url %>

<%=f.submit%>

<%end%>

It took me a little while to wrap my head around this part. Now that we’re being passed an array, we need to add the ability to accept nested attributes to the User class, the code looks as follows:

class User < ApplicationRecord

has_many :repos

accepts_nested_attributes_for :repos

end

The user controller now needs to permit an array for repos as well, and we need to change the create method in the controller.

private def user_params

params.require(:user).permit(:name, repos:[])

end

The controller will need to iterate through the chosen id’s from the application’s user input, find them from the Repo class, and then shovel into the user.repos array.

def create

@user = User.create(name: user_params[:name]) user_params[:repos].each {|repo| @user.repos << Repo.find_by(id:

repo) }

redirect_to @user

end

The final display will show all of the check boxes, so if you have a large pool to choose from, it may not be the best solution for starting out. I’d recommend looking into how to make the check boxes scrollable to allow for a better visual experience.

Since you will often times be using a similar structure for forms there is a way to reuse the same form through partials. The way to do this is to navigate to views and create a new view called _form.html.erb . Then, in any other view you can simply call it through using <%= render ‘form’ %> and it will save you having to rebuild the same code for new/edit.