Rails and Autocomplete
I recently wrote a simple rails app to let people record measurements (current and voltage) on for a given plate. Each plate can have many measurements and each measurement belongs to only one plate. I wanted to make it easy for people to enter the data and there might be a fair number of plates, so I didn’t want people to have to scroll down a long drop down menu to pick a plate. This seemed like an ideal situation to use autocomplete on. Basically, the home screen is just the new measurements path. So, putting in the measurements will be easy. I then wanted to have a text field for the plate name that has autocomplete enabled. As the user enters in information for each plate, a little drop down list will popup that users can use to pick the correct plate. And if it’s a new plate, it will automatically add it to the database.
I had avoided doing something like this for a long time because I knew it would require javascript and I know absolutely none. However, I recently subscribed to Railscasts and watched an episode on autocomplete (#102). It didn’t look too difficult and I gave it a try.
app/assets/javascripts/application.js<pre class=”brush: javascript> //= require jquery //= require jquery-ui //= require jquery_ujs //= require_tree . </pre>
Just had to add the jquery-ui line.
app/assets/javascripts/measurements.js.coffee
jQuery -> $('#measurement_plate_name').autocomplete source: $('#measurement_plate_name').data('autocomplete-source')
Note that coffeescript IS whitespace dependent. Not knowing that probably caused the most difficulty in the entire process. Each one of those tabs above are four spaces. I usually use tabs that are two spaces and it didn’t work.
app/models/measurement.rb
belongs_to :plate attr_accessible :current, :voltage, :note, :plate_name def plate_name plate.try(:name) end def plate_name=(name) self.plate = Plate.find_or_create_by_name(name) if name.present? end
I’m creating a virtual attribute to use in the form to set the plate name.
app/views/measurements/new.html.erb
<%= form_for (@measurement), :url => create_measurement_path, :method => :post do |f| %><%= f.label :plate_name %> <%= f.text_field :plate_name, data: {autocomplete_source: Plate.order(:name).map(&:name)} %>
<%= f.label :current %> <%= f.text_field :current %>
<%= f.label :voltage %> <%= f.text_field :voltage %>
<%= f.label :note %>
<%= f.text_area :note, size: '30x5' %><%= f.submit 'Submit' %>
<% end %>
I deleted some extra stuff in my form (error checking, etc.) because this is the important stuff that needs to be there.
app/assets/stylesheets/measurements.css.scss
ul.ui-autocomplete { position: absolute; list-style: none; margin: 0; padding: 0; border: solid 1px #999; cursor: default; li { background-color: #FFF; border-top: solid 1px #DDD; margin: 0; padding: 0; a { color: #000; display: block; padding: 13px; } a.ui-state-hover, a.ui-state-active, a.ui-state-focus { background-color: #FFFCB2; } } }
I had a hard time getting highlighting to work on the drop down list that appears. I had to add the a.ui-state-focus (line 18) to the css given in the Railscast to get it to work. I would have posted that on the site, but you have to log in with a github account and it just seemed like a hassle.
That’s basically it.