Rails nested_forms not displaying fields when dynamically adding extra objects - ruby-on-rails

I'm currently using the nested_forms gem and I'm trying to be able to add multiple landlords to a property.
At the moment the associations are quite deep:
Property -> Landlord -> Contact_Detail -> Address
In my Property controller I'm building the associations and the initial form is displayed correctly. However, after using the add fields button, there are no fields. I know it is something to do with the object not getting built, but I can't understand why.
Here's my Property model:
belongs_to :address
belongs_to :estate_agent
belongs_to :property_style
has_and_belongs_to_many :landlord
has_and_belongs_to_many :tenancy_agreement
attr_accessible :landlord_attributes, :address_attributes, :estate_agent_attributes,
:property_style_attributes, :sector, :reference , :occupied, :available_date, :property_style_attributes,...
accepts_nested_attributes_for :landlord, :address, :estate_agent, :property_style, :tenancy_agreement
And here's the new function in the Property controller:
def new
#property = Property.new
#property.build_address
#property.landlord.build.build_contact_detail.build_address
#property.estate_agent_id = current_user.estate_agent_id
respond_to do |format|
format.html # new.html.erb
format.json { render json: #property }
end
end
I've had quite a few attempts at this, but can't see where I'm going wrong, is it a problem with the nested_form gem not supporting this many levels of association or the type of association?
Thanks!
EDIT
Changes made:
belongs_to :address
belongs_to :estate_agent
belongs_to :property_style
has_and_belongs_to_many :landlords
has_and_belongs_to_many :tenancy_agreements
attr_accessible :landlords_attributes, :address_attributes, :estate_agent_attributes,
:property_style_attributes, :sector, :reference , :occupied, :available_date, :property_style_attributes,...
accepts_nested_attributes_for :landlords, :address, :estate_agent, :property_style, :tenancy_agreements
Properties controller:
#property.landlords.build.build_contact_detail.build_address
Landlords model
has_and_belongs_to_many :properties
Here is my view:
<%= nested_form_for(#property) do |f| %>
<% if #property.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#property.errors.count, "error") %> prohibited this property from being saved:</h2>
<ul>
<% #property.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<h2>Landlords</h2>
<%= f.fields_for :landlords %>
<p><%= f.link_to_add "Add a Landlord", :landlords %></p>
<div class="actions">
<%= f.submit %>
</div>
<% end %>

Unless you've specified "landlord" as an irregular inflection, Rails will assume that it is singular. Many-to-many associations should be declared in the plural.
Try changing the many-to-many associations to:
has_and_belongs_to_many :landlords
has_and_belongs_to_many :tenancy_agreements
You'll also need to change all calls to these to be plural as well. In addition, you must change the accepts_nested_attributes_for to landlords, and the attr_accessible from landlord_attributes to landlords_attributes.

I attempted to use both awesome-nested-forms and cocoon and it still wouldn't work.
In the end, I found a workaround by building the object in the partial and not in the controller. Like this:
<% f.object.build_contact_detail.build_address %>
I hope this helps someone else!

Related

Rails: Passing nested attribute parameters

So im working through the Odin Project's "Flight Booker" project. https://www.theodinproject.com/courses/ruby-on-rails/lessons/building-advanced-forms. Which essentially is what it sounds like and im running into a problem with passing nested attributes.
First and foremost the Relevant Models:
class Booking < ApplicationRecord
belongs_to :passenger
belongs_to :flight
accepts_nested_attributes_for :passenger
end
class Flight < ApplicationRecord
has_many :bookings, dependent: :destroy
has_many :passengers, through: :bookings
belongs_to :to_airport, class_name: 'Airport', foreign_key: 'origin_id'
belongs_to :from_airport, class_name: 'Airport', foreign_key: 'destination_id'
end
class Passenger < ApplicationRecord
has_many :bookings, dependent: :destroy
has_many :flights, through: :bookings
end
The passenger schema just contains an email and name for right now. But the problem is when I pass the information to the "booking" controller. Here is my "New" form for booking.
<%= form_for #booking do |f| %>
<%= f.hidden_field :flight_id, value: params[:booking][:flight_num] %>
<%= f.hidden_field :passengers_num, value: params[:booking][:passengers_num] %>
<% params[:booking][:passengers_num].to_i.times do |passenger| %>
<%= fields_for :passenger do |passenger| %>
<%= passenger.label :name, 'Name', class: "Label" %>
<%= passenger.text_field :name %>
<%= passenger.label :email, 'email', class: "Label" %>
<%= passenger.email_field :email %>
<% end %>
<% end %>
<%= f.submit "Book Flight" %>
<% end %>
(Ignore the hidden fields for now, they are passed from the "Flights" search page and Im getting those just fine.)
So I am getting the multiple forms (name and email fields) but when I "Submit" I am only getting parameters for the last field sets. (So if there are 3 sets of name/email fields, I only get parameters for the last one).
It's possible im not understanding the fields_for however as I can't find a ton of good examples.
Thanks!
There could be many issues with your implementation...I'll layout a few...
Move <% params[:booking][:passengers_num].to_i.times do |passenger| %> logic into the new action of your bookings controller...ie
def new
#booking = Booking.new
3.times { #booking.passengers.new } # or whatever your logic is to display x amount of passenger fields
end
Make sure that in your bookings controller you are permitting the nested attributes like this...
params.require(:booking).permit(passengers_attributes: [:name, :email])
As far as the form, you'll need to treat it like a form within a form (makes sense...nested attributes created from a nested form!) and use the block variable...like this
<ul>
<%= f.fields_for :passengers do |passenger_form| %>
<li>
<%= passenger_form.label :name
<%= passenger_form.text_field :name %>
</li>
<!-- other permitted fields -->
<% end %>
</ul>

Create 2 types of resources using the same view

I have 2 models (and resources) - Institute and Admin.
I want to have a view with 1 submit button that creates 2 types of resources. Would I need to have 2 separate forms? An example would be great!
Also, what naming convention should this view use (given that it creates 2 types resources).
There is a "has-many through" association between Institute and Admin.
What you want is a design pattern called Form Object.
https://robots.thoughtbot.com/activemodel-form-objects
With a Form Object, you can create a class that represents the form, validate the data and then persist to the resource (or resources) that you need.
There's also a gem called Virtus for that. For me, it's a overkill if what you want is simple. You could just create a ActiveModel model and do your stuff.
Would I need to have 2 separate forms?
Answer is Non. you can make one form nested.
So example : Gessing your "has many through" association like this: One institue has many admins throuth mettings
Models :
class Institute < ActiveRecord::Base
has_many :mettings
has_many :admins, :through => :mettings
accepts_nested_attributes_for :mettings
end
class Admin < ActiveRecord::Base
has_many :mettings
has_many :institues, :through => :mettings
accepts_nested_attributes_for :mettings
end
class Metting < ActiveRecord::Base
belongs_to :institue
belongs_to :admin
accepts_nested_attributes_for :institues
end
Controller :
def new
#institue= Institue.new
#metting= #institue.mettings.build
#admin = #metting.build_admin
end
def create
Institue.new(institue_params)
end
def institue_params
params.require(:institue).permit(:id, mettings_attributes: [:id, :metting_time, admin_attributes: [:id ] )
end
Views can be called _form.erb.rb included in edit.erb.rb:
<% form_for(#institue) do |institue_form| %>
<%= institue_form.error_messages %>
<p>
<%= institue_form.label :name, "Institue Name" %>
<%= institue_form.text_field :name %>
</p>
<% institue_form.fields_for :mettings do |metting_form| %>
<p>
<%= metting_form.label :metting_date, "Metting Date" %>
<%= metting_form.date_field :metting_date %>
</p>
<% metting_form.fields_for :admin do |admin_form| %>
<p>
<%= admin_form.label :name, "Admin Name" %>
<%= admin_form.text_field :name %>
</p>
<% end %>
<% end %>
<p>
<%= institue_form.submit 'Create' %>
</p>
<% end %>
<%= link_to 'Back', institues_path %>

Writing Nested Form in RoR

I'm trying to write a nested form in ROR.
I have two tables Employee and EmployeeInfo and both table have a column named employeeID
these tables are connected with this key.
What i want to do is to create a form with some input fields which should update the values into both tables.
for eg i want a form which can create or update fields named employee_name, age, address and city But employee_name and age are present in table Employee and city and address are present in table EmployeeInfo.
So how should i write the form tag inorder to do this.
Please be sorry if question is a blunder. I'm realy new to this. Pls help
Extending #emu's answer
Models setup
#employee.rb
Class Employee < ActiveRecord::Base
has_one :employe_info
accepts_nested_attributes_for :employee_info
end
#employee_info.rb
Class EmployeeInfo < ActiveRecord::Base
belongs_to :employee
end
Controller
Class EmployeesController < ApplicationController
def new
#employee = Employee.new
#employee.build_employee_info
end
def create
#employee = Employee.new(employee_params)
if #employee.save
redirect_to #employee
else
render 'new'
end
end
private
def employee_params
params.require(:employee).permit(:employee_name, :age, employee_info_attributes: [:id, :city,:address])
end
end
In rails 4 you need to use
accepts_nested_attributes_for :employeeinfo
in your employee model. And also employee has the relation with emplyeeinfo is has_one.
in the form:
<%= form_for #employe, :html => { :multipart => true } do |f| %>
<% if #employe.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#employe.errors.count, "error") %> prohibited this employe from being saved:</h2>
<ul>
<% #employe.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :employee_name %><br>
<%= f.text_field :employee_name %>
</div>
<div class="field">
<%= f.label :age %><br>
<%= f.text_field :age %>
</div>
<%= f.fields_for :employeeinfo do |s| %>
<%= s.label :address %><br>
<%= s.text_field :address %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Please edit the form objects according to your model name.
Foriegn Key
Firstly the "connector" key you're referring to is called a foreign_key.
This is a standard relational database feature, allowing you to "join" two or more databases together with a single reference point. Whenever you use associations in Rails, you'll basically have to join the two datatables with a foreign_key
both table have a column named employeeID
Your employee_infos table only needs to have the foreign_key employee_id:
#app/models/employee.rb
Class Employee < ActiveRecord::Base
has_one :employee_info #-> foreign key = "employee_id"
accepts_nested_attributes_for :employee_info
end
#app/models/employee_info.rb
Class EmployeeInfo < ActiveRecord::Base
belongs_to :employee
end
Forms
As mentioned by Emu, you'll need to use accepts_nested_attributes_for
This allows you to pass data from a "parent" model to a child model, exactly what you have set up currently. The difference being you have to ensure you have it set up correctly.
Regardless of what you think, this is how you need to do it. You mention yourself that you're very new to Rails; which means your objections are likely based on your current DB setup. This might not be correct
You should use emu & Pavan's answers to fix this :)

Rails associations has_many :through

I'm getting started with Ruby on Rails and I have encountered an issue with the has_many :through association.
The models I'm using are:
class Phrase < ActiveRecord::Base
attr_accessible :event_type_id, :template_pieces
belongs_to :event_type
has_many :phrases_pieces
has_many :template_pieces, :through => :phrases_pieces
end
class TemplatePiece < ActiveRecord::Base
attr_accessible :datatype, :fixed_text, :name
has_many :phrase_pieces
has_many :phrases, :through => :phrases_pieces
end
class EventType < ActiveRecord::Base
attr_accessible :name
has_many :phrases
end
class PhrasesPiece < ActiveRecord::Base
attr_accessible :order, :phrase_id, :template_piece_id
belongs_to :phrase
belongs_to :template_piece
end
And I'm trying to create a new phrase, editing its default form to:
<%= form_for(#phrase) do |f| %>
<% if #phrase.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#phrase.errors.count, "error") %> prohibited this phrase from being saved:</h2>
<ul>
<% #phrase.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
Select the event type:
<%= collection_select(:phrase, :event_type_id, EventType.all, :id, :name) %>
Select the phrases to be used:
<%= collection_select(:phrase, :template_pieces, TemplatePiece.all, :id, :name) %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
I first had an issue with Mass Assignment, but I fixed that adding the attr_accessible :template_pieces to the phrases model. I'm unsure if that is the correct way of fixing it, but at least it stopped complaining that it could not mass assign the protected attribute.
Now, I'm getting the following error when submitting a new phrase:
undefined method `each' for "1":String
Which I kinda think happens due to the fact that there are supposed to be many template_pieces for a given phrase, but I'm currently only able to submit them one at a time. So it just finds the one, tries to iterate through it and fails.
How would I go about fixing that? Is there a better way of entering models with the has_many :through to the database? Do I have to do it manually (as in dismissing the default controller #phrase = Phrase.new(params[:phrase])?
Thanks!
You should use the fields_for helper to wrap the nested attributes:
<%= f.fields_for :template_pieces do |template_f| %>
<%= template_f.collection_select, :event_type_id, EventType.all, :id, :name %>
Select the phrases to be used:
<%= template_f.collection_select, :template_pieces, TemplatePiece.all, :id, :name %>
<% end %>
Reference
fields_for documentation.

Radio buttons for a has_many and belongs_to association

I have a has_many and belongs_to association.
class Link < ActiveRecord::Base
has_and_belongs_to_many :categories
belongs_to :property
end
class Property < ActiveRecord::Base
has_many :links
end
In the index and show I have <%= link.property.name %> and it will show the Property that I assigned to the link with the console just fine.
I have a problem with figuring out how to show radio buttons in the _form that assign a property to the link (a drop down would work as well).
It seems everyone who has had this question before has ether a has_many :through or a HABTM relationship and I can't seem to adapt their answers.
Since each link has only one property, you probably want radio buttons (not check boxes). This should work (in your view)
<%= form_for #link do |f| %>
<% #properties.each do |p| %>
<%= f.radio_button :property_id, p.id %>
<%= f.label :property_id, p.name %>
<% end %>
<%= f.submit %>
<% end %>
Don't forget to set #properties = Property.all in your controller.

Resources