Topic: Multipage forms?

I have a rather large table/model which also requires a very large form to fill out, after a bit of research it seems that a multipage form is what I want. However, all the posts regarding this subject in the context of rails seem rather dated; So I was wondering what the de facto (if such a thing exists) way to do multipage forms are? I was thinking of building up an activerecord object throughout the form pages and then saving it at the end, but this seems too easy (although it could be). Any ideas?

Re: Multipage forms?

jcapote wrote:

I was thinking of building up an activerecord object throughout the form pages and then saving it at the end, but this seems too easy (although it could be). Any ideas?

That's how I would do it, however, you will come across a couple stumbling blocks. One being validation. To solve the validation problem, you can create several validation methods, one for each page. For example, if you have an Order model which contains billing and shipping information (two pages in the controller) you can split the validation up into two methods:

def validate_billing
  #...
  errors.empty?
end

def validate_shipping
  #...
  errors.empty?
end


You can then call these methods on each controller action. An alternative is to set the state in an instance variable and validate accordingly. This allows you to make use of the validation helper methods (such as validates_presence_of). For example:

class Order < ActiveRecord::Base
  validates_presence_of :ship_address_id, :if => :shipping_state?
  validates_presence_of :bill_address_id, :if => :billing_state?
 
  def shipping_state?
    @state == :shipping
  end

  def billing_state?
    @state == :billing
  end

  def valid_in_state?(state)
    @state = state
    valid?
  end
end


You can even expand upon this and allow the model to hold an array of states so you can validate them all at the end, etc. However, rendering the appropriate page with the form fields containing the errors can be a pain, then what if multiple the validation errors span across multiple pages?

One more alternative, then I'll shut up. Skip the validation on the multipage form until the very end, then, if there are any errors, display a page containing all the fields for the model (or better yet, just the fields that need fixing). However, this doesn't work well if some fields depend upon the value of other fields.

Edit: Okay, I lied. One more variation is to split the model up into smaller models (one for each page). This allows you to do more of a RESTful design. However, the main problem with this is, if someone drops out in the middle of the process, you will have hanging models (because a model is saved through each page in the process).

Last edited by ryanb (2006-09-26 21:30:45)

Railscasts - Free Ruby on Rails Screencasts

Re: Multipage forms?

Amazing response. If it's not too much trouble, I'd like you to elaborate on the last validation method, that seems more up my alley (no fields depend on each other).

As for the actual form, I've decided to go with a tabber system where its actually one large single page form, that gets split up into different "Steps" in the form process where one can go back and forth between steps within a single page, (http://www.barelyfitz.com/projects/tabber/), the last tab/step being a submit button.

Last edited by jcapote (2006-09-27 02:47:41)

Re: Multipage forms?

Looks like those tabs are just managed with divs, so you can easily display the same page without the tabs (just one long list of fields). You can do this on the last page if there are any validation errors so all fields are visible.

Then the question is, how do you display only the fields where there is an error? It's a little tedious, but one way to do this is have an if condition to only display the field if there's an error on it. Like this:

<% form_for :my_model %>
...
<% if @my_model.errors.empty? || @my_model.errors.invalid? :some_attribute %>
  <p>Some Field: <%= f.text_field :some_attribute %></p>
<% end %>
...
<% end %>

You can extract this into a helper to make it a little easier:

# in helper
def display_field?(model_object, attribute)
  model_object.errors.empty? || model_object.errors.invalid? attribute
end

# in view
<% if display_field? @my_model, :some_attribute %>
  <p>Some Field: <%= f.text_field :some_attribute %></p>
<% end %>


You may also want to consider creating a FormBuilder that handles all of this if the form layout is predictable (that is, the label can be generated, etc.). If you can create a form builder, that would shorten the above to:

<% form_for :my_model, :builder => MyFormBuilder %>
...
<%= f.text_field :some_attribute %>
...
<% end %>

Railscasts - Free Ruby on Rails Screencasts

Re: Multipage forms?

This thread has helped me a great deal.

I have another issue related to this - how could I allow the user to add files associated with their model when it hasn't been saved yet?  These might be Jpegs or PDFs and could be quite large files...

TIA,

Matt.

Re: Multipage forms?

Depends on what plugin/technique you're using, but I believe most of them store the files in some temporary location on disk if the model hasn't been saved yet. This is in case validation fails or something.

Railscasts - Free Ruby on Rails Screencasts

Re: Multipage forms?

I'll have to roll my own. Each image needs to be stored in small, medium and large versions.

Perhaps I could use the user's session id to name a temp directory containing a hierarchy of the files and then trigger the code to move them to their permanent home using after_create() on their parent object?

Many thanks,

Matt.

Re: Multipage forms?

I would also need some cron'ed job to delete all the old files, which I could associate with the job that removes the stale sessions from the sessions table...

Sorry, just thinking out loud in the hope it might help someone else, too! ;-)

Re: Multipage forms?

I think that will work. I'm not too familiar with how the current plugins work, so you might want to view the source for them and see how they reference the file when it's in the temporary directory.

Railscasts - Free Ruby on Rails Screencasts

Re: Multipage forms?

IIRC file_column may behave similarly.  I'll have a look at that.

Thanks for your help.  Much appreciated.

Re: Multipage forms?

I am a novice at Rails, also working on a multi-page form, and am having trouble with the validation. The way the form works is that the first step of the form creates an object, and each additional step updates that object (this is going to be a very low volume app, so high performance is not a concern). I have tried the idea of tracking the "state" of the program, but for some reason this is not working.

Thanks in advance for your help,
Ford

Re: Multipage forms?

ryanb wrote:

... One more variation is to split the model up into smaller models (one for each page). This allows you to do more of a RESTful design. However, the main problem with this is, if someone drops out in the middle of the process, you will have hanging models (because a model is saved through each page in the process).

What if you were OK with this behaviour, and allowed someone to half-fill out the overall multipage form flow; the user can return at another time and modify/finish it.  For example, say you have a survey with multiple sections, each on a separate page and possibly represented by a different model.  How would you be able to determine if you need to create a new model for the next page, or edit an already existing one for that user?  I've considered this in the past and just ended up creating all the (empty) models first, so all the user was doing was editing as they went along.  But this isn't particularly good practice, especially where validation is concerned! What other alternatives can someone suggest for managing this?

Re: Multipage forms?

Is the user related to the models in some way? You need a way to keep track of the models already created so you can reference them when the user comes back. Then it's just a matter of having on "if" condition to either create or update the record depending on if one already exists.

Railscasts - Free Ruby on Rails Screencasts

Re: Multipage forms?

Yes, the records were related back to the user in a hierarchical fashion:

I had survey_response (with user id)
[-> section (which question/answer_groups are shown on a page - like a form of pagination)]
-> answer_group (based on questions that are grouped with the same options to choose from)
-> answer.

This is now getting off topic from the original multipage forms query - rather it's ordered multi-model "instances" (the answer_groups and their answers) within the one form on a page (thanks to your excellent articles on this BTW).

I found it to be tricky when you deal with these answer_groups and answers in an ordered list, related to the ordering of the questions.  I will consider how to re-engineer it with some if conditions to check for existence of records for each answer_group and individual answers, and move away from ordered lists to a kind of array representation via question ids.

Last edited by magenta (2007-06-21 01:37:54)

Re: Multipage forms?

jcapote wrote:

Amazing response. If it's not too much trouble, I'd like you to elaborate on the last validation method, that seems more up my alley (no fields depend on each other).

As for the actual form, I've decided to go with a tabber system where its actually one large single page form, that gets split up into different "Steps" in the form process where one can go back and forth between steps within a single page, (http://www.barelyfitz.com/projects/tabber/), the last tab/step being a submit button.

I have an identical problem and i will defenitevely try that javascript tabs but my question is how do you code the form itself? You wrap a form element around the main javascript div and then put the various form fields in each internal div/tab? At the end so form a rails point of view it is a single page form so you code it normally right?

16

Re: Multipage forms?

Thanks for this!

Just found it very useful, I wanted to do some ajax form validation but keeping everything in one place, I.e. keep all validation stuff in the ActiveRecord object.

The method of validating depending on "state" / page is the one i've gone for, and is working great so far.

Cheers.