I'm having a bit of a challenge with understanding accepts_nested_attributes_for.
I have a Resident class that is composed of various child classes (address, doctor, guardian, etc). My understanding of the steps necessary to make accepts_nested_attributes_for work is as follows:
- Create the necessary association(s)
- Add accepts_nested_attributes_for :resource to the parent class whose form I will use to commit the nested attributes
- whitelist nested attributes like so. attr_accessible :address_attributes, :doctor_attributes, etc...
- In the parent controller, build the associated resources. i.e. @resident.addresses.build (for has_many associations), @resident.build_doctor (for has_one association)
- add a fields_for block for each child resource to include their attribute values.
I have the address working for my a resident record, but my challenge arises when I try to add the other classes. For instance, doctor also needs an address and so does guardian.
Would I also add accepts nested_attributes_for :address to my doctor.rb model? And if so, would that mean that I would need a controller for this resource also invoking build methods for it's components? And would this be the time to use nested fields_for in my form template? (example follows)
<%= f.fields_for :doc1 do |doc| %>
<%= doc.text_field :fname %>
<%= doc.text_field :lname %>
<%= doc.fields_for :address do |doc_address| %>
<%= doc_address.text_field :street_address %>
<%= doc_address.text_field :city %>
<%= doc_address.text_field :state %>
<% end %>
<%= doc.fields_for :primary_phone do |phone1| %>
<%= phone1.phone_field :area_code %>
<%= phone1.phone_field :number %>
<% end %>
<% end %>
Here are the associated files:
resident.rb
class Resident < ActiveRecord::Base
attr_accessible :address_attributes, :guardian_attributes, :desrep_attributes, :doctor_attributes,
:em_contact1_attributes, :em_contact2_attributes, :fname, :lname, :gender, :pic, :soc, :dob,
:marital_stat, :placement_name, :placement_address, :res_start_date,
:res_end_date, :religious_prefs, :insurance_info, :burial_provisions,
:case_number, :vet_stat_num, :admission_height, :admission_weight,
:resident_initials, :allergies,:admin_id
belongs_to :admin
has_one :address, as: :addressable
has_one :guardian
has_one :desrep, class_name: "DesignatedRepresentative"
has_one :doc1, class_name: "Doctor"
has_one :em_contact1, class_name: "EmergencyContact"
has_one :em_contact2, class_name: "EmergencyContact"
has_one :primary_phone, class_name: "PhoneNumber"
has_one :secondary_phone, class_name: "PhoneNumber"
has_many :assessment_plan_forms, dependent: :destroy
has_many :blood_pressure_record_forms, dependent: :destroy
has_many :fund_record_form1s, dependent: :destroy
has_many :fund_record_form2s, dependent: :destroy
has_many :incident_accident_forms, dependent: :destroy
has_many :med_record_forms, dependent: :destroy
has_many :personal_care_forms, dependent: :destroy
has_many :resident_care_agreement_forms, dependent: :destroy
has_many :visitation_appointment_forms, dependent: :destroy
has_many :weight_record_forms, dependent: :destroy
accepts_nested_attributes_for :address, allow_destroy: true
accepts_nested_attributes_for :guardian, allow_destroy: true
accepts_nested_attributes_for :desrep, allow_destroy: true
accepts_nested_attributes_for :doc1, allow_destroy: true
accepts_nested_attributes_for :em_contact1, allow_destroy: true
accepts_nested_attributes_for :em_contact2, allow_destroy: true
validates_presence_of :fname, :lname
def full_name
"#{ fname } #{ lname }"
end
def guard_fname
guarian.fname
end
end
address.rb
class Address < ActiveRecord::Base
attr_accessible :street_address, :city, :state, :zip, :addressable_type, :addressable_id
belongs_to :addressable, polymorphic: true
end
doctor.rb
class Address < ActiveRecord::Base
attr_accessible :street_address, :city, :state, :zip, :addressable_type, :addressable_id
belongs_to :addressable, polymorphic: true
end
emergency_contact.rb
class EmergencyContact < ActiveRecord::Base
attr_accessible :address_attributes, :primary_phone_attributes, :secondary_phone_attributes, :fax_attributes,
:fname, :lname, :primary, :email, :resident_id
belongs_to :resident
has_one :address, as: :addressable
has_one :primary_phone, class_name: "PhoneNumber"
has_one :secondary_phone, class_name: "PhoneNumber"
has_one :fax, as: :phoneable
accepts_nested_attributes_for :address
accepts_nested_attributes_for :primary_phone
accepts_nested_attributes_for :secondary_phone
accepts_nested_attributes_for :fax
end
residents_controller.rb - (build code is inside the 'new' method)
class ResidentsController < ApplicationController
before_filter :authenticate_admin!
def index
@residents = Resident.all
end
def new
@resident = Resident.new
@resident.build_address
@resident.build_guardian
@resident.build_desrep
@resident.build_em_contact1
@resident.build_em_contact2
end
end
views/residents/_form.html.erb (this shows the working address fields_for for a resident)
<%= f.fields_for :address do |builder| %>
<div class="control-group">
<%= builder.label :street_address, "Address:", class: "control-label" %>
<div class="controls">
<%= builder.text_field :street_address, class: "text_field", placeholder: "Resident's Address" %>
</div>
</div>
<div class="control-group">
<%= builder.label :city, "City:", class: "control-label" %>
<div class="controls">
<%= builder.text_field :city, class: "text_field", placeholder: "Resident's City" %>
</div>
</div>
<div class="control-group">
<%= builder.label :state, "State:", class: "control-label" %>
<div class="controls">
<%= builder.text_field :state, class: "text_field", placeholder: "Resident's State" %>
</div>
</div>
<div class="control-group">
<%= builder.label :zip, "Zip:", class: "control-label" %>
<div class="controls">
<%= builder.text_field :zip, class: "text_field", placeholder: "Resident's Zip Code" %>
</div>
</div>
<% end %>
views/residents/_form.html.erb ( this shows the doctor object as 'doc1', that isn't working)
<%= fields_for :doc1 do |doc| %>
<div class="control-group">
<%= doc.label :fname, "Doctor's First Name:", class: "control-label" %>
<div class="controls">
<%= doc.text_field :fname, class: "text_field", placeholder: "Doctor's First Name" %>
</div>
</div>
<div class="control-group">
<%= doc.label :lname, "Doctor's Last Name:", class: "control-label" %>
<div class="controls">
<%= doc.text_field :lname, class: "text_field", placeholder: "Doctor's Last Name" %>
</div>
</div>
<div class="control-group">
<%= doc.label :initials, "Initials:", class: "control-label" %>
<div class="controls">
<%= doc.text_field :initials, class: "text_field", placeholder: "Doctor's Initials" %>
</div>
</div>
<div class="control-group">
<%= doc.label :phone1, "Doctor's Primary Phone:", class: "control-label" %>
<div class="controls">
<%= doc.phone_field :phone1_area_code, placeholder: "Area Code", style: "width: 25%;" %>
<%= doc.phone_field :phone1_number, class: "text_field", placeholder: "i.e. 800-555-1212" %>
</div>
</div>
<div class="control-group">
<%= doc.label :phone2, "Doctor's Secondary Phone:", class: "control-label" %>
<div class="controls">
<%= doc.phone_field :phone2_area_code, placeholder: "Area Code", style: "width: 25%;" %>
<%= doc.phone_field :phone2_number, class: "text_field", placeholder: "i.e. 800-555-1212" %>
</div>
</div>
<div class="control-group">
<%= doc.label :fax, "Doctor's Fax:", class: "control-label" %>
<div class="controls">
<%= doc.phone_field :fax_area_code, placeholder: "Area Code", style: "width: 25%;" %>
<%= doc.phone_field :fax_number, class: "text_field", placeholder: "Doctor's Fax Number" %>
</div>
</div>
<div class="control-group">
<%= doc.label :email, "Doctor's Email:", class: "control-label" %>
<div class="controls">
<%= doc.text_field :email, class: "text_field", placeholder: "Doctor's Email Address" %>
</div>
</div>
<div class="control-group">
<%= doc.label :street_address, "Doctor's Address:", class: "control-label" %>
<div class="controls">
<%= doc.text_field :street_address, class: "text_field", placeholder: "Doctor's Street Address" %>
</div>
</div>
<div class="control-group">
<%= doc.label :city, "Doctor's City:", class: "control-label" %>
<div class="controls">
<%= doc.text_field :city, class: "text_field", placeholder: "Doctor's City" %>
</div>
</div>
<div class="control-group">
<%= doc.label :state, "Doctor's State:", class: "control-label" %>
<div class="controls">
<%= doc.text_field :state, class: "text_field", placeholder: "Doctor's State" %>
</div>
</div>
<div class="control-group">
<%= doc.label :zip, "Doctor's Zip:", class: "control-label" %>
<div class="controls">
<%= doc.text_field :zip, class: "text_field", placeholder: "Doctor's Zip Code" %>
</div>
</div>
<% end %>
Before using accepts_nested_attributes for I wanted to simply lazy load my objects in or pass them through the initializer. But there is more reading I'll have to do first, because I could not get the results I wanted using those techniques so far.
I can show the remaining view code and any additional files upon request.
Thanks in advance.
Update: 05-25-14 - I am still working on figuring this out, and have written this blog post about where I'm at currently with my understanding.
