Category software development

I spent time over the past week building a form that went through some interesting iterations. The form involved a few nested associations, and had initially been built using JavaScript to incorporate drop downs, but my team decided to go back to a simpler Rails form and rebuild functionality from there. I’ll walk through the steps I took, and the tools I used along the way, to reach our current iteration.


My first step was to change the application to use SimpleForm rather than the standard Rails form. SimpleForm is a cool gem that both simplifies and adds functionality to Rails forms. SimpleForm provides input helpers like labels, hints, and input_fields, and also lets you use the same options that you could use for each field type in Rails forms. For example, in SimpleForm

= f.input :school

will generate the same HTML as the following:

= f.label :school
= f.text_field :school

SimpleForm refers to the database to choose the best column type for each input, and you may overwrite this by passing in a different input like this:

f.input :school, as: :check_boxes

SimpleForm also makes it easy to display associations, which I initially thought would be useful for our purposes. This is a nice feature of SimpleForm, but we wanted to be able to do more with our associations, including adding autocomplete functionality, so I decided to add Cocoon as the next step in creating our form.


Cocoon is a gem that can make it easier to build nested forms. I initially was tripped up by some of the details of adding Cocoon to the code, but after figuring it out with the first model, the rest of my work with it went fairly quickly.

The code I added for Cocoon touched the model, controller, and views. Cocoon counts on the fact that the partial for nested associations will live within specific CSS elements, but since I only wanted a user to be able to add or remove associations, not edit them, I had to do a little customizing and initially did not place my form components in the correct elements. I found that it is relatively simple to customize forms that include Cocoon, but be sure to place your code in the proper elements so it will work as expected.

My initial code in the schools/_form partial was similar to this:

  = f.simple_fields_for :teachers do |teacher|
    = render 'teacher_fields', f: teacher
    = link_to_add_association 'Add Teacher', f, :teachers
= f.submit

And my teacher_fields partial looked like this:

  = f.input :first_name
  = f.input :last_name
  = link_to_remove_association "Remove Teacher", f

However, that code allowed the teacher to be edited in the partial, and I only wanted to display the teacher’s name, with the ability to remove or add teachers. I changed my form code to simply display teacher information, and I ended up only using the teacher_fields partial for adding a new teacher. Cocoon’s link_to_add_association automatically looks for the related partial and the nested-fields element, so I kept the partial for the purpose of adding new teachers:

= f.simple_fields_for :teachers do |teacher|
    td.teacher-name= teacher.object.user.full_name
    td.remove-association= link_to_remove_association  "Remove", teacher, data: { confirm: 'Are you sure?' }
  = link_to_add_association "Add Teacher", f, :teachers

Lastly, another place where it’s possible to trip up when using Cocoon is around strong parameters. In order for the association to be found, edited, and destroyed, the nested association needs to be included in parameters as below, given my above school model with associated teachers:

def school_params
  teachers_attributes: [:id, :first_name,
 :last_name, :_destroy])


After adding Cocoon, the last step in our initial iteration of the form was to add autocomplete functionality for nested associations. I used rails-jquery-autocomplete to accomplish this. You’ll notice some extra options in the SchoolsController in my code examples below. I wanted each teacher to show up by full_name, a method on the teacher model, but autocomplete works with model attributes, so I used first_name as the model attribute for autocomplete. Autocomplete only returns the specified column and the id, so I can pass in last_name as extra data that is needed for the full_name method to work. I can also tell the controller to use full_name as the display value in the form.

class SchoolsController < Admin::BaseController
  autocomplete :teacher, :first_name, extra_data:
  [:last_name], display_value: :full_name, full: true

By adding this line to the SchoolsController, I’ve created a controller action that needs to be added to my routes file:

resources :schools do
  get :autocomplete_user_first_name, on: :collection

Finally, I can add the new autocomplete functionality to my teacher_fields partial, which will generate the necessary HTML for my form:

  = f.input :first_name, url:
    id_element: '#user_id', as: :autocomplete
  = f.input :last_name
  = link_to_remove_association "Remove Teacher", f

After adding SimpleForm, Cocoon, and rails-jquery-autocomplete, we had a form that listed each teacher’s name with a corresponding button to remove that teacher, and a button to add new teachers at the end of the form. I’d recommend all three of these gems for building forms, and would encourage you to dig into the documentation and see how they can be customized to meet your needs. In the short term, this was a functional form since we didn’t have a lot of data, but as more teachers are added and as our client’s app grows, this form would grow to be ungainly. Enter Selectize…


Selectize, which calls itself “the hybrid of a textbox and select box,” allowed me to remove both Cocoon and rails-jquery-autocomplete in order to simplify my form and make it easier for the user to quickly add or remove multiple associations.

The changes in my form code are below. You’ll notice the changes to the teacher input that name the collection to use, how to label each item in the collection to be displayed, and allow for multiple selections.

  = f.input :name
  = f.input(:teacher_ids,
            label: "Teachers",
            collection: current_school.teachers,
            label_method: :full_name,
            input_html: {multiple: true}
= f.submit

Using Selectize led to the collection of teachers chosen being submitted as an array, so I had to change the parameters in the school controller to allow for this.

def school_params
  params.require(:school).permit(:name, {teacher_ids:

Outside of these changes, the only other thing I had to do was create a JavaScript file for the schools form, and Selectize works some magic.

$(function() {

We now have an iteration of our form that will be more sustainable for this application, and easier for users, as it grows to deal with more data over time.