I am using the nested_form_fields gem. I have the following associations to allow a contact
to have associations to multiple 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
A contact always Must have at least one team
. I created a custom validation which checks for this when the user is creating a new contact
or is updating an existing 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
It works for the most part. But there is an edge case when updating an existing contact's team. Here I am updating a contact and it shows that they have two existing associated teams:
The user clicks the X's next to each team in order to delete them, so they no longer show on the page, and then the user clicks update to put these changes into effect:
The validation properly fails because the user attempted to delete all the associated teams. It properly shows the validation error message:
However, the problem is that the form does not redisplay the existing associated teams! Those associations have not been deleted yet, so they should still show:
How do I show existing team associations after validations fail when updating an existing contact?
I attempted removing all the _destroy
flags in hopes that by doing so those associated teams will show. Unfortunately it didn't do the trick:
# 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
I am thinking that the page is remembering the nested_form_fields' javascript that was previously run. So it remembers that the teams were marked to be deleted, therefore it is not rendering them. I don't know how to reset the javascript unless I do a redirect
, in which case all the validation errors will no longer be shown.
Thanks in advance!
Here is the answer. I changed the name of the custom validation within the Contact
model so that the error message would make sense:
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
And then within the update
action of the ContactsController
:
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