views:

557

answers:

3

Hello,

can somebody explain me, how to do wizard-style forms with validations?

f.e.:

i have a appointment-model, which should include following data: name, starttime, endtime, address, city.

Now i want to have 3 actions for each datagroup:

  1. name
  2. start and enddate
  3. address-fields

each of them should be accessible (so AASM isn't an option I think -> model.wizard_step = 3 and generating the right view for this step isnt enough).

And each of them should have validations

  1. name shouldnt be nil
  2. startdate must not be after enddate
  3. addressfields must not be nil

but how do i save this object between the single steps, and how to build in this validations schemas/groups...?

heeelp.. thanks =)

A: 

You're going to have to basically "roll your own" on this one. The closest you can get with standard Rails / ActiveRecord functionality is to do something like the following:

class WizardController < ApplicationController
  def wizard_1
    foo = Foo.new params[:foo]
    foo.valid?

    if %{name}.any? {|att| foo.errors.on(att) }
      # failed pseudo-validation
    else
      render :wizard_2
    end
  end

  def wizard_2
    foo = Foo.new params[:foo]
    foo.valid?

    if %{name startdate enddate}.any? {|att| foo.errors.on(att) }
      # failed pseudo-validation
    else
      render :wizard_3
    end
  end

  def wizard_3
    # standard rails Controller#create
  end
end

And you'll have to pass along the variables from each step into hidden fields in the next. You might also want to consider simply doing this in one stage, rather than in three.

Stephen Touset
Yeah, but with this solution, i cant assign the steps stateless...f.e. i want to access the address-form, but without filling in the start and enddate...
Lichtamberg
A: 

can't you just use :if ?

e.g.

validates_presence_of :name
validates_presence_of :address_fields, :if => Proc.new {|p| p.startdate && p.enddate}

def validate
  unless name.blank?
    errors.add(:startdate) if startdate > enddate 
  end
end
Omar Qureshi
+1  A: 

Combine a hidden field in your form:

f.hidden_field :wizard_stage, :value => '<current action>'

with something like this

class Appointment < ActiveRecord::base

   attr_accessor :wizard_stage

   validates_presence_of :name, :if => lambda{|a| a.wizard_stage == 'name'}
   validates_presence_of [:start, :end], :if => lambda{|a| a.wizard_stage = 'dates'}
end
Tor Erik Linnerud
but isnt that a bit unsecure?If the user removes the wizard_stage-hidden field there are no validations happening?
Lichtamberg