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.
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.
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.
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
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.