Forum: Ruby on Rails Best way to split forms into steps?

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Robbie S. (Guest)
on 2006-04-26 13:27
I have a form with about 30+ fields, which the client is wanting to
separate into 3 pages/steps - just wondering what is the best way to go
about this, specifically remembering data entered, from one page to
another, and making sure the various bits of data are validated
correclty, error messages outputted etc etc...

is there a best practice method for doing this?
Chris R. (Guest)
on 2006-04-26 13:53
Are the 30 fields all from 1 model?
Robbie S. (Guest)
on 2006-04-26 14:13
Chris R. wrote:
> Are the 30 fields all from 1 model?

at the moment yes, I have one table..but if there's a better way then
its no biggy to change it.

I was almost thinking of having 3 different tables/models/controllers
for each step, where step3  belongs_to step2 which belongs_to step1.
This way I could do the validations on each step, if successful then do
a redirect to the next step...really not sure which is the best way to
do this though
Chris R. (Guest)
on 2006-04-26 14:29
I'd store the model in the session.  I'd do it like this :

def new1
  @employee = Employee.new
end

def create1
  @employee = Employee.new(params[:employee])  #get the first 10 fields
  ...
  insert code here that would fill up the 20-30 fields with valid empty
data
  ...
  if @employee.valid?
    session[:employee]=@employee
    redirect_to :action=>"new2"
  else
    render :action=>"new1"
  end
end

def new2
end

def create2
  @employee=session[:employee]
  ...
  insert code to empty the 10-20 fields from @employee
  ...
  @employee.update_attributes(params[:employee])  #get fields 10-20
  if @employee.valid?
    session[:employee]=@employee
    redirect_to :action=>new3
  else
    render :action=>"new2"
  end
end

def new3
end

def create3
  @employee=session[:employee]
  ...
  insert code to empty the 20-30 fields from @employee
  ...
  @employee.update_attributes(params[:employee])  #get fields 20-30
  if @employee.save
    flash[:notice] = 'Employee was successfully updated.'
    .......
  else
    render :action=>"new3"
  end
end

This code could do with some improving, but it gives a general idea of
how you could do it.
Robbie S. (Guest)
on 2006-04-26 14:35
thanks very much!
Mike G. (Guest)
on 2006-04-26 18:45
(Received via mailing list)
On 4/26/06, robbie shepherd <removed_email_address@domain.invalid> wrote:
>
> I have a form with about 30+ fields, which the client is wanting to
> separate into 3 pages/steps - just wondering what is the best way to go
> about this, specifically remembering data entered, from one page to
> another, and making sure the various bits of data are validated
> correclty, error messages outputted etc etc...
>
> is there a best practice method for doing this?



I had to do the exact same thing.  I found two different articles
regarding
how to implement this type of thing:

<
http://www.kylemaxwell.com/articles/2006/02/06/fun...
>

and

<http://www.bigbold.com/snippets/posts/show/277>

I tried the first link but wasn't able to get it to work (and I also
preferred the second method).  I changed the second method to make it a
bit
more dynamic.  Here's what I'm using right now, which also does
validation
on the fields before creating the new record (by the way, if anyone else
has
any other methods for implementing multi-part forms, please post them!):

class UserController < ApplicationController
  before_filter :clear_stage_errors, :get_partial_user_from_session
  after_filter :save_partial_user_in_session

  def initialize
    # here we provide the names of the fields that we want to be
validated
    # at each stage, where stage1 corresponds to the fields listed in
@stages[0]
    @stages = [ %w(first_name last_name address1 country city state
zip_code
birth_date phone1 email),
                %w(radio_stations personal_genres),
                %w(live_performance_frequency),
                %w(employment_experience ),
                %w(some_field some_other_field)
              ]
  end

 def signup
   case @request.method
   when :post
     # current_stage is stored in a hidden form field
     @current_stage = @params[:current_stage].to_i

     # if user hit the previous button, decrement the current stage
     if @params['imgPrevious.x']
       @current_stage -= 1
       return;
     end

     @user.attributes = @params['user']

     if (@current_stage  >= @stages.length)  &&
(valid_for_attributes(@user,
@stages[@current_stage - 1]))

       if @user.save
         # if we don't reset the @user ivar to nil, whenever we access
the
signup
         # form, it keeps repopulating the contents of the fields with
the
previously
         # entered information
         @user = nil
         # you might not want to reset the session here, if so, remove
this
next line
         reset_session
         flash['notice']  = "Signup successful"
         redirect_to :action => :index, :controller => "site"
         return
       end
     else
       # move on to the next stage only if we can validate all the
attributes for the current stage
       if (valid_for_attributes(@user, @stages[@current_stage - 1]))
         @current_stage += 1
       end
     end

   when :get
     # since the request is a get, we fetch the first stage
     @current_stage = 1
   end
 end

   def get_partial_user_from_session
     unless @session['partial_user'].nil?
       @user = @session['partial_user']
     else
       @user = User.new
     end
   end

   def save_partial_user_in_session
     unless @user.nil?
       @session['partial_user'] = @user
     end
   end

   # Might be a good addition to AR::Base
   def valid_for_attributes( model, attributes )
     unless model.valid?
       errors = model.errors
       our_errors = Array.new
       errors.each { |attr,error|
         if attributes.include? attr
           our_errors << [attr,error]
         end
       }
       errors.clear
       our_errors.each { |attr,error| errors.add(attr,error) }
       return false unless errors.empty?
     end

     return true
   end
end

in models/user.rb
class User < ActiveRecord::Base

  validates_presence_of :first_name, :last_name, :address1, :city,
:state,
  :country, :zip_code, :phone1, :email, :birth_date, :radio_stations,
  :personal_genres, :live_performance_frequency

end


in views/user/signup.rhtml:

<%= start_form_tag({:action=> "signup"} , { :name => 'signupform',
:multipart => true } ) %>
<%= hidden_field_tag "current_stage", @current_stage %>
<%= @flash['notice'] %>
<%= render_partial "stage#{@current_stage}" %>
<div id="bottomNavigation" style="padding-top:10px;">
      <% if @current_stage != 1 %>
      <input type="image" name="imgPrevious"
src="/images/common/button_previous.gif" alt="Previous" />
      <% end %>
      <input type="image" name="imgNext"
src="/images/common/button_next.gif" alt="Next" />
    </div>
<%= end_form_tag %>


and then place each of your stages with your text_fields, checkboxes,
form
elements, etc into views/user/[_stage1.rhtml, _stage2.rhtml,
_stage3.rhtml,
...] ie:

in _stage1.rhtml:

<table>
  <tr>
    <td><label for="first_name">First Name:</label></td>
    <td ><%= text_field 'user', 'first_name' %></td>
  </tr>
  <tr>
    <td><label for="middle_initial">Middle Initial:</label></td>
    <td ><%= text_field 'user', 'middle_initial' %></td>
  </tr>
</table>

good luck,

Mike
This topic is locked and can not be replied to.