Best way to split forms into steps?


#1

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?


#2

Are the 30 fields all from 1 model?


#3

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


#4

thanks very much!


#5

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.


#6

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-with-single-table-inheritance

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}” %>

<% if @current_stage != 1 %> <% end %>
<%= 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:

First Name: <%= text_field 'user', 'first_name' %>
Middle Initial: <%= text_field 'user', 'middle_initial' %>

good luck,

Mike