Multi-steps forms aka wizards

Piotr Wlodarek wrote:

As of now, what is the best approach to create multi-step
creation/edition forms (wizards)?

Googling does not provide a clear answer.

I have found the REST/CRUD model does not work well with multi-page forms (wizards). What I have found is generally best is to implement custom non-CRUD actions which display the various steps and navigation between the steps (a DSL would be good here but I have never bothered to create one). Then store the attributes as a hash in the session. Each page can add to the hash as you go. If the user goes to a previous page the fields will be populated from the hash.

Then when the process is complete and you have all the data you want you would post to the create action to move to the more traditional CRUD for the completion and actual creation of the object submitting the results of the hash as the arguments to the create.

At least that is what has worked for me.

Eric

This is very inefficient solution. In each step when u submit the
data, save it in the db and save the id in the session. In the next
step load the record from the db using the id stored in the session.
Now update that record. Continue the same procedure for all steps.

There is a blog post that shows how to use acts as state machine for
workflow.

Bcp wrote:

This is very inefficient solution. In each step when u submit the data, save it in the db and save the id in the session. In the next step load the record from the db using the id stored in the session. Now update that record. Continue the same procedure for all steps.

There is a blog post that shows how to use acts as state machine for workflow.

Not really sure how it is any more inefficient. Either way the data has to be stored (session or database). Code-wise they have about the same complexity (i.e. not much). If you are using a server-side storage mechanism for the session (vs. the new cookie method) then you are not increasing traffic any. So what is inefficient.

The big win with the session method is that your model does not have to be valid until all steps are done. This allows you to use the normal validation stuff without resorting to hacks.

But whatever works for you. Just offering up my method.

Eric

But when there's a problem with the data, don't you now a big headache (and the user too)?. You have to figure out what page to go to, and he doesn't get feedback until he's done.

What I did was set a page_number in the controller, and then the validation is like this:

validates_presence_of :state, :if => :page_1

and in the model

   def page_1
     page_number >= 1
   end

It has the pleasant side-effect of making it easy for incomplete entries to see where a person stopped.

What I've actually found to be a really helpful way to do it is to have post-backs for each step which you check for validity and either re-render or redirect on to the next step, while saving it in the session along the way. For example:

class PostsController < ApplicationController
   def step_1
     if request.get?
       @post = Post.new
     elsif request.post?
       @post = Post.new(params[:post])
       if @post.valid?
         session[:post] = @post
         redirect_to step_2_wizard_path and return
       else
         flash.now[:error] = @post.errors
       end
     end
   end

   def step_2
     @post = session[:post]
     # etc...
   end
end

This may seem wrong to RESTful developers (multiple request types in one action) but I've found it to be the most succinct way to do multi-step forms, not to mention the benefit of being able to post your form to the same URL you're already on (aka you don't even need to set the :url in a form_for).

If you wanted a more RESTFUL approach I would suggest something along the lines of:

class PostsController < ApplicationController
   def step_1
   end

   def save_step_1
   end

   # ... etc.
end

with something like this in your routes:

map.resources :posts, :collection => {:step_1 => :get, :save_step_1 => :post} # etc.

My thoughts on the problem, anyway.

Michael Bleigh
michael@intridea.com

The "one model per wizard step" that you adpoted would suggest that
there is very little data dependency between the steps. If that's the
case, why not render all the steps at once (separate divs) and use
some js to show/hide divs as you need to? By doing that, you can use
'next' and 'prev' steps and the data does not take a roundtrip to re-
populate.

Raul Martinez wrote:

Does anyone know about the "act_as_wizard" plug-in?? Any experience??

http://github.com/Adkron/actsaswizard/tree/master

Tanks a lot,
Raul.

I searched and tried with acts_as_wizard plugin.
You can use wizardly gem at http://github.com/jeffp/wizardly

But I found both of them not working in rails(3.0.0rc). Does anybody
have any idea of such plugins for Rails(3.0.0rc) ?

What about the railscast on multistep-forms. I think it was rails3.

<http://railscasts.com/episodes/217-multistep-forms>

Piotr Wlodarek wrote:

Thanks for tips. Finally I resorted to "one model per wizard step"
approach.

Usually not a good idea. It's generally better to keep track of which
step you're on and save the (one) model as you go along.

Best,

Marnen Laibow-Koser wrote:

Piotr Wlodarek wrote:

Thanks for tips. Finally I resorted to "one model per wizard step"
approach.

Usually not a good idea. It's generally better to keep track of which
step you're on and save the (one) model as you go along.

Best,
--
Marnen Laibow-Koser
http://www.marnen.org
marnen@marnen.org

Sorry for commenting on such an old post -- forgot to check date.