2
\$\begingroup\$

I've building a multi-step form that saves data in multiple models. The "base" or parent model is the Venue, and has_one or has_many other models. In the screen below, the "Basic Information" tab is the only one which is mandatory to fill. Once that is saved, a row is generated in the Venue table in the database, and the Venue.id is used in the remaining tabs for the associations. The other tabs are "good to have" data. When I click on Save Changes, the form data is POSTed via ajax and a success message div is shown, and I avoid reloading the page.

Page with 5 tabs and a form in each tab

In order to render only the form when the different tabs are clicked, I've used Ajax. My _form.html.erb looks like this:

<div class="row-fluid">
  <ul id="venueEditNav" class="nav nav-pills nav-stacked span3">
    <li class="active"><%= link_to 'Basic Information', {:action => 'new_basic'}, remote: true %></li>
    <li><%= link_to 'Function Spaces', {:action => 'new_halls'}, remote: true %></li>
    <li><%= link_to 'Pricing & Terms', {:action => 'new_pricing'}, remote: true %></li>
    <li><%= link_to 'Amenities', {:action => 'new_amenities'}, remote: true %></li>
    <li><%= link_to 'Account Settings', {:action => 'new_settings'}, remote: true %></li>
  </ul>
  <div class="span7">
    <div id="venueContent">
      <%= render partial: 'venues/basic', locals: { :venue => @venue } %>  <!-- default -->
    </div>    
  </div>
</div>

For each tab, I created:

  1. Two .js.erb files - e.g: new_basic.js.erb, edit_basic.js.erb
  2. One partial - e.g: _basic.html.erb

Both the .js.erb files have the same one liner code plus any javascript specific to that form:

$("#venueContent").html('<%=j render partial: 'venues/basic', locals: { :venue => @venue } %>')

The partial file will have the actual form code. The files for the other tabs are similar.

My controller looks like this:

class VenuesController < ApplicationController
  #new, index, show and other actions are unchanged from default, snipped here for brevity

  def new_basic
    @venue = Venue.new
    @venue.build_address
    @venue.build_contact

    respond_to do |format|
      format.js   #new_basic.js.erb
    end
  end

  def new_halls
    @venue = Venue.new
    @hall = Hall.new
    #@hall.venue_id = @venue.id

    respond_to do |format|
      format.js   #new_halls.js.erb
    end
  end

  def create
    @venue = Venue.new(params[:venue])

    respond_to do |format|
      if @venue.save
        format.html { redirect_to @venue, notice: 'Venue was successfully created.' }
        format.json { render json: @venue, status: :created, location: @venue }
      else
        format.html { render action: "new" }
        format.json { render json: @venue.errors, status: :unprocessable_entity }
      end
    end
  end

  def edit_basic
    @venue = Venue.find(params[:id])

    respond_to do |format|
      format.js   #edit_basic.js.erb
    end
  end

  def edit_halls
    @venue = Venue.find(params[:id])

    respond_to do |format|
      format.js   #edit_halls.js.erb
    end
  end
end

I did this in my routes.rb:

resources :venues do
    member do   #member - requires an ID
      get 'edit_basic'
      get 'edit_halls'
      ...
    end
    collection do   #works on a collection, does not require an ID
      get 'new_basic'
      get 'new_halls'
      ...

    end
end

This works so far, but for 5 tabs, I've 15 files. Also, in my _form.html.erb, I've to write code to distinguish between new and edit and link to that action correctly. I'm not sure how well it will work, but before I go down that route, this whole approach looks hackish. Am I doing this the RAILS way? Is there a better way to do this?

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.