【问题标题】:Partial validations in multistep forms (Wizard)多步表单中的部分验证(向导)
【发布时间】:2020-01-17 10:51:32
【问题描述】:

我有一个使用向导创建的多步骤表单。基本上,表单的第一步是 user/sign_up - 据我了解,这还不是一步。点击注册按钮后,用户移动到“真正的”第一步,即:address.

    class UserStepsController < ApplicationController
    include Wicked::Wizard
    steps :address


    def show
      @user = current_user || User.from_omniauth(request.env["omniauth.auth"])
      render_wizard
    end


    def update
      @user = current_user || User.from_omniauth(request.env["omniauth.auth"])
      @user.update!(user_params)
      render_wizard @user
    end

    private

    def user_params
        params.require(:user).permit(:email, :password, :password_confirmation, :remember_me, :first_name, :last_name, :street, :house_number, :city, :zip_code)
    end

    def redirect_to_finish_wizard(options = nil, params = nil)
        redirect_to new_user_profile_path(current_user)
    end

end

这基本上是表格的结尾。所有内容都保存给用户。现在我被验证困住了。

class User < ApplicationRecord

  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable,
         :omniauthable, omniauth_providers: %i[facebook]

    has_one :profile, dependent: :destroy
    after_create :create_profile
    accepts_nested_attributes_for :profile
    validates :first_name, presence: true
    validates :last_name, presence: true
    validates :street, presence: true
    validates :house_number, presence: true
    validates :city, presence: true
    validates :zip_code, presence: true

    def self.from_omniauth(auth)
        where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
            user.email = auth.info.email
            user.password = Devise.friendly_token[0, 20]
            name = auth.info.name
            user.first_name = name.split(" ")[0]
            user.last_name = name.split(" ")[1]

        end
    end
end

我很想在我的模型中使用条件验证,并且只在某个步骤上验证存在。这应该很容易,因为理论上我只有一步,即地址。我在互联网上找到的所有内容都太复杂了。问题是,我是否必须以某种方式将 user/sign_up 更改为表单中的第一步,而地址将是第二步?还是这样就好了?如果是这样,我可以在验证中将“if”语句添加到地址属性中,以某种方式定义地址步骤吗?会这样吗?

def on_address_step?
wizard.steps = wizard.steps.first
end

或者我如何定义它?验证将如下所示:

validates :first_name, presence: true
    validates :last_name, presence: true
    validates :street, presence: true, if: :on_address_step?
    validates :house_number, presence: true, if: :on_address_step?
    validates :city, presence: true, if: :on_address_step?
    validates :zip_code, presence: true, if: :on_address_step?

这肯定不是那么容易。目前这也行不通。我需要如何更改它?谢谢。 P.S:这也是我的用户控制器:

class UsersController < ApplicationController

  def index
    @users = User.all
  end

  def new
    @user = User.new
  end

  def create
    @user = User.new(params[:user])
    if @user.save
      session[:user_id] = @user.id
      redirect_to user_steps_path
    else
      render :new
    end
  end

  private

  def user_params
    params.require(:user).permit(:email, :password, :password_confirmation, :remember_me, :first_name, :last_name, :street, :house_number, :city, :zip_code)
  end

end

【问题讨论】:

  • 请明确你想要得到的输出。这是否就像用户使用凭据注册然后表单发送到第二步一样。我认为您可以不使用 Wicked::Wizard 来做到这一点。
  • 我已经在使用 gem 了。我有我的验证,但在第一步我只想验证名字,姓氏。在第二步中,我希望验证与地址相关的所有数据 - 应该填写这些字段。现在它在第一步中验证了地址和名称。
  • 使用validates :street, presence: true, on: :on_address_step? 更新您的验证规则。将if: 更改为on:
  • 我需要定义:on_address_step?不同。目前这个方法行不通。
  • 这是Gem提供的方法吗?

标签: ruby-on-rails


【解决方案1】:

如果填写地址是一个完全独立的过程,我只会将地址分支到它自己的模型和控制器中。

class User < ApplicationRecord
  # ...
  has_one :address
end

class Address < ApplicationRecord
  # ...
  belongs_to :user
  validates :first_name, :last_name, :street, 
            :house_number, :city, :zip_code, presence: true
end

这避免了将您的用户模型变成更多的上帝对象,并消除了对条件验证的需要,这使您的模型比应有的更了解用户体验步骤。

# routes.rb
resources :addresses, only: [:new, :create]

class UsersController < ApplicationController
  # ...
  def create
    @user = User.new(params[:user])
    if @user.save
      session[:user_id] = @user.id
      redirect_to new_address_path
    else
      render :new
    end
  end
end

class AddressesController < ApplicationController
  # You should have some sort of method that checks if the user
  # is signed in and redirect otherwise
  before_action :authenticate_user!

  # GET /addresses/new
  def new
    # I'm assuming you have some sort of method to fetch the signed in user
    @address = current_user.build_address
  end

  # POST /addresses
  def create
    @address = current_user.build_address(address_params)
    if @address.save
      redirect_to '/somepath'
    else
      render :new
    end
  end

  def address_params
    params.require(:address).permit(
      :first_name, :last_name, :street, 
      :house_number, :city, :zip_code
    )
  end
end

<%= form_with(model: @address) %>
  # ... inputs
<% end %>

我怀疑您是否真的想要使用 Wicked 所涉及的复杂性,如果您确实需要长的多步骤表单,这没关系,但在这种情况下,有一个更简单和更好的设计选择。

【讨论】:

  • 我明白你的意思。但对我来说,了解更多关于这些形式的信息很重要。所以,我真的希望我能解决这个问题,并且仍然拥有我的多步骤表单。
  • 实际问题是,注册是一个注册控制器动作(创建),而填写地址并保存,是用户步骤控制器动作(更新)。这个特别问题的答案很简单,我把它贴在这里:link。也许你感兴趣。再次感谢您的帮助!
猜你喜欢
  • 2018-11-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多