3
votes

Consider a User model with the following fields:

  • First name (required)
  • Last name (required)
  • Email (required)
  • Password (required)
  • Phone (required, size: 10 digits)
  • Address (required)

And a multi-step signup form with the following steps:

  • 1st step with fields First Name, Last Name, and Email
  • 2nd step with Password, Phone and Address.

How would you create a solution to validate the input in each step?

The standard ActiveRecord's way doesn't works, since it validates all fields at once.

I've created a solution for this problem but it turned out in a complicated code, so I'm looking for alternatives.

1
Lambda conditional based on a step attribute? Granted, you might turn out to have some complicated controller code. validates :first_name, presence: true, if: lambda { |user| user.step === 1 }. If you find a good answer, the Rails community would love to know :) - Justin
Do you need to have a User instance all the way through? You could build one or two temporary non-User models along the way and then convert them to a real User when you're done. Same logic overall but the conditionalness of the validation would be handled by the separation of the models rather than a bunch of complexity in User. - mu is too short

1 Answers

2
votes

Personally I would let the model manage it's own validation and move the logic for managing validations for each step to that.

Now, this is probably not the most elegant way to do it but its effective and doesn't clutter the code too much.

Add a step transient attribute to the model.

attr_accessor :step

Add the following method to the model to check if on right step:

def on_step(step_number)
  step == step_number or step.nil?
end

The reason for step.nil? - this has the advantage that if you want to use validation on this model without using steps simply don't assign a value for step on your model and the method will allows return true enabling the validation to always be carried out.

Change validations to process only if on right step or to bypass if not using steps

validates :first_name, if: "on_step 1", presence: true
validates :last_name,  if: "on_step 1", presence:true
validates :email,      if: "on_step 1", presence:true

validates :password,   if: "on_step 2", presence:true
validates :phone,      if: "on_step 2", format:{ with: TEL_REGEX  }, allow_blank: false
validates :address,    if: "on_step 2", presence:true

Of course don't forget to set the current step for the model, for example by hard-coding it in a hidden field of the form (if rendering separate forms for each step) and change your params to receive it.