Nested_form_fields 在验证失败时显示现有的 has_many 嵌套字段

Nested_form_fields show existing has_many nested fields upon validation failure

我正在使用 nested_form_fields gem。我有以下关联,允许 contact 与多个 teams 关联:

#models/contact.rb
class Contact < ActiveRecord::Base
  has_many :contacts_teams
  has_many :teams, through: :contacts

  accepts_nested_attributes_for :contacts_teams, allow_destroy: true
end

#models/contacts_team.rb
class ContactsTeam < ActiveRecord::Base
  belongs_to :contact
  belongs_to :team
end

#models/team.rb
class Team < ActiveRecord::Base
  has_many :contacts_team
  has_many :contacts, through: :contacts_teams
end

一个联系人总是必须至少有一个team。我创建了一个自定义验证,用于在用户创建新的 contact 或更新现有的 contact.

时进行检查
#custom validation within models/contact.rb
class Contact < ActiveRecord::Base
  ...
  validate :at_least_one_contacts_team

  private
  def at_least_one_contacts_team
    # when creating a new contact: making sure at least one team exists
    return errors.add :base, "Must have at least one Team" unless contacts_teams.length > 0

    # when updating an existing contact: Making sure that at least one team would exist
    return errors.add :base, "Must have at least one Team" if contacts_teams.reject{|contacts_team| contacts_team._destroy == true}.empty?
  end           
end

大部分情况下都有效。但是在更新现有联系人的团队时存在一种边缘情况。我在这里更新联系人,它显示他们有两个现有的关联团队:

用户单击每个团队旁边的 X 以删除它们,因此它们不再显示在页面上,然后用户单击更新以使这些更改生效:

验证正常失败,因为用户试图删除所有关联的团队。它正确显示验证错误消息:

但是,问题是表单不会重新显示现有的关联团队!这些关联还没有被删除,所以它们应该仍然显示:

更新现有联系人时验证失败后如何显示现有团队关联?

我试图删除所有 _destroy 标志,希望通过这样做,那些相关的团队会显示出来。不幸的是,它没有成功:

# block run when update validation fails
else
  params[:contact][:contacts_teams_attributes].each do |k,v|
    v.delete_if{|k,v| k == "_destroy" && v == "1"}
  end
  render :edit
end

我认为该页面正在记住之前 运行 的 nested_form_fields' javascript。所以它记得团队被标记为要删除,因此它不会渲染它们。我不知道如何重置 javascript 除非我执行 redirect,在这种情况下将不再显示所有验证错误。

提前致谢!

答案在这里。我在 Contact 模型中更改了自定义验证的名称,以便错误消息有意义:

class Contact < ActiveRecord::Base

  accepts_nested_attributes_for :contacts_teams, allow_destroy: true
  validate :at_least_one_contacts_team

  private

  def at_least_one_contacts_team
    return errors.add :A_Team, "must be present" unless contacts_teams.length > 0
    return errors.add :A_Team, "must be present" if contacts_teams.reject{|contacts_team| contacts_team._destroy == true}.empty?
  end
end

然后在 ContactsControllerupdate 操作中:

def update
  authorize @contact
  if @contact.update(contact_params)
    redirect_to(some_path), success: 'Updated Successfully.'
  else
    # This next line is what makes it work as expected for that edge case
    @contact.contacts_teams.reload if @contact.errors.keys.include?(:A_Team)
    render :edit
  end
end