13.3. JavaScript and CoffeeScript in the Asset Pipeline

The easiest way of explaining how you go about programming with JavaScript and the asset pipeline in a Rails project is by using a little example. As always, the main focus is not on creating an amazingly meaningful application. ;-)

Changing Form Depending on Input

Let's build a room reservation where you can book a single or double room and then have to enter either one or two guest names in the same form. The basic structure:
$ rails new hotel
  [...]
$ cd hotel 
$ rails generate scaffold reservation start:date end:date room_type:string guest_name1 guest_name2
  [...]
$ rake db:migrate
  [...]
$
The aim is to display the following page when you go to http://0.0.0.0:3000/reservations/new:
Form with a single room
As soon as the user selects a double room instead of a single, we want a second name field to appear:
Form with a double room
So I am changing two things in the app/views/reservations/_form.html.erb:
  • I request the room_type via
    f.select(:room_type, options_for_select(['single room', 'double room']))
  • In the div element around the second name, I set an ID with
    <div class="field" id='second_name'>
Here is the whole code for the form:
<%= form_for(@reservation) do |f| %>
  <% if @reservation.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@reservation.errors.count, "error") %> prohibited this reservation from being saved:</h2>

      <ul>
      <% @reservation.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :start %><br />
    <%= f.date_select :start %>
  </div>
  <div class="field">
    <%= f.label :end %><br />
    <%= f.date_select :end %>
  </div>
  <div class="field">
    <%= f.label :room_type %><br />
    <%= f.select(:room_type, options_for_select(['single room', 'double room'])) %>
  </div>
  <div class="field">
    <%= f.label :guest_name1 %><br />
    <%= f.text_field :guest_name1 %>
  </div>
  <div class="field" id='second_name'>
    <%= f.label :guest_name2 %><br />
    <%= f.text_field :guest_name2 %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>
In the file app/assets/javascripts/reservations.js.coffee I define the CoffeeScript code that toggles the element with the ID second_name between visible (show) or invisible (hide) depending on the content of reservation_room_type:
jQuery ->
  $('#second_name').hide()
  $('#reservation_room_type').change ->
    room_type = $('#reservation_room_type :selected').text()
    if room_type == 'single room'
      $('#second_name').hide()
    else
      $('#second_name').show()

Note

In reality, you would surely integrate the guest names in a 1:n has_many association, but in this example we just want to demonstrate how you can change the content of a form via JavaScript.

Updates about this book will be published on my Twitter feed.