Rails 4 Nested Form not Passing Nested Attributes in Params - ruby-on-rails-4

When I submit the form it is passing {"utf8"=>"✓", "authenticity_token"=>"blah", "client"=>{"first_name"=>"jack", "last_name"=>"kool","complaints"=>{"symptom"=>"burnt"} in the params and I am getting Unpermitted Parameters: complaints Since complaints is nested in Client, it should be passing complaints_attributes, like I have it set up in the strong params, and I can't figure out why it isn't.
class ClientsController < ApplicationController
def new
#client = Client.new
end
def edit
#client = Client.find(params[:id])
end
def create
#client = Client.new(client_params)
if #client.save
redirect_to #client
else
render 'new'
end
end
def update
#client = Client.find(params[:id])
if #client.update(client_params)
redirect_to #client
else
render 'edit'
end
end
private
def client_params
params.require(:client).permit(:first_name, :last_name, complaints_attributes: [ :symptom, :id ])
end
end
Client model:
class Client < ActiveRecord::Base
has_many :complaints
has_one :personal_disease_history
has_one :family_disease_history
has_many :surgeries
has_many :hospitalizations
has_many :medications
has_many :allergies
validates :first_name, :last_name, presence: true
accepts_nested_attributes_for :complaints
end
Complaint model:
class Complaint < ActiveRecord::Base
belongs_to :client
end
Form:
<%= form_for :client, url: clients_path, html: { class: "form-inline" } do |f| %>
<div class="panel panel-success">
<div class="panel-heading">
<h3 class="panel-title">Health History Form</h3>
</div>
<div class="panel-body">
<div class="form-blocks"><legend>Name</legend>
<div class="form-group">
<%= f.label :first_name, class: "sr-only" %>
<%= f.text_field :first_name, class: "form-control", placeholder: "First Name" %>
</div>
<div class="form-group">
<%= f.label :last_name, class: "sr-only" %>
<%= f.text_field :last_name, class: "form-control", placeholder: "Last Name" %>
</div>
</div>
<div class="form-blocks"><legend>Complaints</legend>
<div class="form-group">
<%= f.fields_for :complaints do |builder| %>
<%= builder.label :symptom, class: "sr-only" %>
<%= builder.text_field :symptom, class: "form-control", placeholder: "Symptom" %>
<% end %>
</div>
</div>
<div>
<%= f.submit "Submit Form", class: "btn btn-primary btn-lg"%>
</div>
<% end %>

inside client controller create action, try saying this inside the if #client.save:
Complaint.create(symptom: params[
"client"]["complaints"]["symptom"], client_id: #client.id)
the unpermitted parameter is related to the nested attribute problem but this should get the actual record saving. If that doesn't do it, please post the stack trace surrounding the commit.
have couple other ideas: but (1) you have complaint_attributes in the permitted params but you could change that to complaint bc that's what's coming through in the params.

Related

Rails 4 Deep Nested Form with Cocoon Not Working

Im attempting to use Cocoon to deeply nest dynamic forms in Rails 4
Im getting an error on the link_to_remove_association function from the 2nd level nested form (_milling_datum_fields).
ERROR: undefined method `new_record?' for nil:NilClass
Sorry for the mass amount of code.
My models of concern.
Project
class Project < ActiveRecord::Base
belongs_to :company, inverse_of: :projects
has_many :project_materials
has_many :materials, through: :project_materials
has_many :finished_materials, class_name: 'Material', through: :setups
has_many :setups
has_many :milling_data, through: :setups
has_many :class_data, through: :setups
has_many :screen_data, through: :setups
has_many :notes, as: :notable
has_many :tasks, inverse_of: :project, dependent: :destroy
accepts_nested_attributes_for :tasks, :notes, allow_destroy: true, reject_if: :all_blank
accepts_nested_attributes_for :materials, :setups, :milling_data, :class_data, :screen_data, :finished_materials, reject_if: :all_blank
Setup
class Setup < ActiveRecord::Base
belongs_to :project
has_one :finished_material, class_name: "Material"
has_one :milling_datum
has_one :class_datum
has_one :screen_datum
accepts_nested_attributes_for :milling_datum, :class_datum, :screen_datum, :finished_material, reject_if: :all_blank
end
MillingDatum
class MillingDatum < ActiveRecord::Base
belongs_to :setup
has_many :notes, as: :notable
My Views
projects/_form.html.erb
<%= form_for(#project) do |f| %>
...
<div class="col-lg-6">
<%= f.fields_for :setups do |setup| %>
<%= render 'layouts/setup_fields', f: setup %>
<% end %>
<div class="links float-e-margins">
<%= link_to_add_association 'add setup', f, :setups, partial: 'layouts/setup_fields', class: "btn btn-outline btn-default btn-xs" %>
</div>
</div>
...
layouts/_seutp_fields.html.erb
<div class="nested-fields">
...
<div class="col-lg-6">
<%= f.fields_for :milling_data do |md| %>
<%= render 'layouts/milling_datum_fields', f: md %>
<% end %>
<div class="links float-e-margins">
<%= link_to_add_association 'add milling data', f, :milling_data, partial: 'layouts/milling_datum_fields', class: "btn btn-outline btn-default btn-xs" %>
</div>
</div>
<div class="col-lg-6">
<%= f.fields_for :class_data do |cd| %>
<%= render 'layouts/class_datum_fields', f: cd %>
<% end %>
<div class="links float-e-margins">
<%= link_to_add_association 'add class data', f, :class_data, partial: 'layouts/class_datum_fields', class: "btn btn-outline btn-default btn-xs" %>
</div>
</div>
<div class="col-lg-6">
<%= f.fields_for :screen_data do |sd| %>
<%= render 'layouts/screen_datum_fields', f: sd %>
<% end %>
<div class="links float-e-margins">
<%= link_to_add_association 'add screen data', f, :screen_data, partial: 'layouts/screen_datum_fields', class: "btn btn-outline btn-default btn-xs" %>
</div>
</div>
<div class="col-lg-6">
<%= f.fields_for :finished_materials do |fm| %>
<%= render 'material_fields', f: fm %>
<% end %>
<div class="links float-e-margins">
<%= link_to_add_association 'add finished material', f, :finished_materials, partial: 'projects/material_fields', class: "btn btn-outline btn-default btn-xs" %>
</div>
</div>
</div>
<%= link_to_remove_association "X", f, class: "btn btn-outline btn-danger btn-xs pull-right" %>
</div>
</div>
layouts/_milling_datum_fields.html.erb
<div class="nested-fields">
...
<div class="field form-group">
<%= f.label :rate_test_lbs_hr %><br>
<%= f.number_field :rate_test_lbs_hr, class: "form-control" %>
</div>
</div>
<%= link_to_remove_association "X", f, class: "btn btn-outline btn-danger btn-xs pull-right" %>
</div>
projects_controller strong params
def project_params
params.require(:project).permit(:name, :scheduled_start_date, :estimated_end_date, :employees_needed, :incoming_packaging, :final_product_packaging, :sample_instructions, :project_description, :project_type, :building_id, :paid_status, :material_total_weight_lbs, :shifts, :shift_hrs, :rate_lb_hr, :company_id,
materials_attributes: [:id, :_destroy, :material_type, :name, :moisture, :density_g_cm3, :msds_url],
notes_attributes: [:id, :_destroy, :content, :notable_id, :notable_type],
setups_attributes: [:id, :_destroy, :project_id,
milling_data_attributes:[
:id, :_destroy, :date_milled, :mill_model, :mill_liner_type, :mill_tlgs_set, :mill_rpm, :mill_amps, :mill_class_rpm, :mill_class_amps, :feeder_model, :feeder_speed_setting, :feeder_auger_diam_inch, :air_pressure_iw_mill_out, :air_pressure_iw_mill_in, :air_pressure_iw_dustcollector_baghouse, :air_pressure_iw_dustcollector_exit, :air_pressure_iw_blower_out, :temp_ambeint, :temp_mill_out, :temp_prod_out, :rate_test_total_lbs, :rate_test_total_time, :rate_test_lbs_hr, :setup_id
],
screen_data_attributes:[
:id, :_destroy, :screen_base_pci_id, :top_weight, :bottom_weight, :weight_lead, :deck_count, :screen_1_mesh, :screen_2_mesh, :screen_3_mesh, :setup_id
],
class_data_attributes: [
:id, :_destroy, :rate_lb_hr, :class_pci_id, :rpm, :secondary_air_setting, :ap_iw_secondary_air_in, :ap_iw_main_exit, :ap_iw_main_entrance, :setup_id
],
finished_materials_attributes: [
:id, :_destroy, :material_type, :moisture, :name, :msds_url, :density_g_cm3, :setup_id
]
])
end
Ive scoured the similar threads on the Internet but I can not figure it out.
Im starting to think I have something wrong in my models.
Would it help if I post my projects_controller.rb ?
Thanks for helping.
Alright Guys I figured out my particular problem.
The issue was that my nested strong params defined in my parent object controller (projects_controller) were not correctly pluralized according the the relationship definitions in my models.
Giving me the error: ERROR: undefined method `new_record?' for nil:NilClass
I had
params.require(:project).permit(:name, :scheduled_start_date, :estimated_end_date, :employees_needed, :incoming_packaging, :final_product_packaging, :sample_instructions, :project_description, :project_type, :building_id, :paid_status, :material_total_weight_lbs, :shifts, :shift_hrs, :rate_lb_hr, :company_id,
setups_attributes: [:id, :_destroy, :project_id,
milling_data_attributes:[
:id, :_destroy, :date_milled, :mill_model, :mill_liner_type, :mill_tlgs_set, :mill_rpm, :mill_amps, :mill_class_rpm, :mill_class_amps, :feeder_model, :feeder_speed_setting, :feeder_auger_diam_inch, :air_pressure_iw_mill_out, :air_pressure_iw_mill_in, :air_pressure_iw_dustcollector_baghouse, :air_pressure_iw_dustcollector_exit, :air_pressure_iw_blower_out, :temp_ambeint, :temp_mill_out, :temp_prod_out, :rate_test_total_lbs, :rate_test_total_time, :rate_test_lbs_hr, :setup_id
])
Since my relations go Project has_many->Setups has_one->Milling_datum I need to change the pluralization of milling_data_attributes to milling_datum_attributes
From looking into this problem I have seen the most common errors with the Coccoon Gem
The inclusion of nested strong params and the correct pluralization of each object according to the relationship defined in the models.
Having your parent model define the correct accepts_nested_attributes_for with the correct pluralization of each object according to the relationship defined in the models.
Using the correct structure of partials, html, and coccoon provided functions according to the cocoon instructions.

rails simple_form using virtual attributes

I am trying to incorporate Simple_forms into my app using a virtual attributes. I am following http://railscasts.com/episodes/102-auto-complete-association-revised to get autocomplete to work as well. Simple_form works when i do not use a virtual attribute but when I do use the virtual attribute I get the error "Association :head_coach_name not found".
My Team Model is:
class Team < ActiveRecord::Base
attr_accessor :head_coach_name
belongs_to :user
validates :team_name, presence: true
belongs_to :head_coach, class_name: "User", :foreign_key => "head_coach_id"
def head_coach_name
user.try(:name)
end
def head_coach_name=(name)
self.user = User.find_by_name(name) if name.present?
end
end
My User model is:
class User < ActiveRecord::Base
has_many :teams, :class_name => "::Team", dependent: :destroy
end
My View:
<%= simple_form_for #team, html: {class: 'form-horizontal' }, url: teams_path, method: 'post' do |f| %>
<%= f.error_notification %>
<%= f.hidden_field :user_id %>
<div class="col-md-6">
<div class="row">
<div class="col-md-12">
<%= f.input :team_name, class: "form-control" %>
<%= f.input :year, collection: Date.today.year-90..Date.today.year %>
<%= f.input :season, as: :select, collection:
['Fall',
'Winter',
'Spring',
'Summer'] %>
<%= f.input :season_type, as: :select, collection:
['Outdoor',
'Indoor',
'Tournament'] %>
<%= f.input :awards %>
</div>
</div>
</div>
<div class="col-md-6">
<div class="row">
<div class="col-md-12">
<%= f.input :club %>
<%= f.input :division %>
<%= f.association :head_coach_name %>
<%= f.input :assistant_coach_id %>
<%= f.input :assistant_coach_two_id %>
</div>
</div>
</div>
<%= f.button :submit, label: "Add team", class: "btn btn-large btn-primary col-md-3 pull-right" %>
<% end %>
Everything works as long as i don't have the virtual association in there. I could put :head_coach and it would work with a drop down list but I want to use the autocomplete feature like in the railscast video. Also in rails console i can run these commands to show the virtual attribute works:
2.1.2 :003 > team.head_coach_name
User Load (0.6ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 100 LIMIT 1
=> "Miss Daisha Shanahan"
Any ideas on how I can get the virtual attribute to work correctly with simple_forms?
You are referencing an association that doesn't exist with f.association :head_coach_name. Try this instead:
f.association :head_coach, label_method: :head_coach_name
You have a few other oddities in your code, including your head_coach_name definition, which should rely on head_coach instead of user. Same concern for the head_coach_name= method.
Example. You have:
def head_coach_name
user.try(:name)
end
Seems like you should have:
def head_coach_name
head_coach.try(:name)
end

accepts_nested_attributes_for not creating attr writer method

my guarantors model has_many addresses as :addressable and accepts nested attributes for addresses. however, when whitelisting the address attributes, the params hash isn't recognizing addresses_attributes: [:street, :city, etc.]. I keep getting unpermitted parameters addresses. anyone have trouble with this?
my models:
class UserApplication < ActiveRecord::Base
belongs_to :user
has_many :guarantors, dependent: :destroy
accepts_nested_attributes_for :guarantors
end
class Guarantor < ActiveRecord::Base
has_many :addresses, :as => :addressable, dependent: :destroy
belongs_to :user_application, :foreign_key => :user_application_id
accepts_nested_attributes_for :addresses
end
class Address < ActiveRecord::Base
belongs_to :addressable, :polymorphic => true
end
from the UserApplications Controller
def user_application_params
params.require(:user_application).permit(...,
guarantors_attributes: [:id, :fname, :lname, :relationship, :phone, :email, :user_application_id,
addresses_attributes: [:id, :street, :street_2, :unit, :city, :state, :zip, :addressable_id, :addressable_type]]
)
end
the 'nested' part of the form:
<%= f.fields_for :guarantors do |builder| %>
<div class="row">
<div class="col-md-1">
<h6>FIRST NAME</h6>
<%= builder.text_field :fname, :class => "form-control" %>
</div>
<div class="col-md-2">
<h6>LAST NAME</h6>
<%= builder.text_field :lname, :class => "form-control" %>
</div>
<div class="col-md-2">
<h6>RELATIONSHIP</h6>
<%= builder.text_field :relationship, :class => "form-control" %>
</div>
<div class="col-md-2">
<h6>PHONE</h6>
<%= builder.number_field :phone, :class => "form-control" %>
</div>
<div class="col-md-2">
<h6>EMAIL</h6>
<%= builder.text_field :email, :class => "form-control" %>
</div>
</div>
<%= f.fields_for :addresses do |builder| %>
<div class="col-md-1">
<h6>ADDRESS</h6>
<%= builder.text_field :street, :class => "form-control" %>
</div>
<div class="col-md-2">
<h6>ADDRESS 2</h6>
<%= builder.text_field :street_2, :class => "form-control" %>
</div>
<div class="col-md-2">
<h6>CITY</h6>
<%= builder.text_field :city, :class => "form-control" %>
</div>
<div class="col-md-2">
<h6>STATE</h6>
<%= builder.text_field :state, :class => "form-control" %>
</div>
<div class="col-md-2">
<h6>ZIP</h6>
<%= builder.number_field :zip, :class => "form-control" %>
</div>
</div>
<% end %>
<% end %>
Finally, params:
"guarantors_attributes"=>{"0"=>{"fname"=>"blah", "lname"=>"", "relationship"=>"", "phone"=>"", "email"=>"", "id"=>"68"}, "1"=>{"fname"=>"", "lname"=>"", "relationship"=>"", "phone"=>"", "email"=>"", "id"=>"69"}}, "addresses"=>{"street"=>"", "street_2"=>"", "city"=>"", "state"=>"", "zip"=>""}, ...
I keep getting unpermitted parameters addresses and it's also not nested within guarantors_attributes. Any ideas? Thanks in advance.
When you build your fields_for :addresses, I think you want that to be builder.fields_for since addresses are nested under guarantors.
f.fields_for :guarantors do |builder|
# fields
builder.fields_for :addresses do |builder|
# fields
end
end
code clarity
I would also make the block names more specific, since having 2 builder variables is confusing as to which scope you are in.
f.fields_for :guarantors do |guarantor_form|
# fields
guarantor_form.fields_for :addresses do |address_form|
# fields
end
end

First argument cannot contain nil or be empty rails 4

I'm making a single page application based on Futureme.org for practice. The user goes to the home page, sees a form to put their email address, subject, and the body of their message and sends it.
The problem I am having is I get an error "First argument in form cannot contain nil or be empty". Here is my code;
Model;
class Letter < ActiveRecord::Base
VALID_EMAIL_REGEX = /\A[\w+\-,]+#[a-z\d\-.]+\.[a-z]+\z/i
validates_presence_of :email, presence: true, format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates_length_of :subject, presence: true, length: { maximum: 50 }
validates_presence_of :message
end
Controller;
class LettersController < ApplicationController
def new
#letter = Letter.new
end
def create
#letter = Letter.new(params[:letter])
if #letter.save
redirect_to letters_path, :notice => "Your letter was sent!"
else
render "new"
end
end
end
View form;
<%= form_for #letter, :html => {:class => 'form-horizontal'} do |f| %>
<% if #letter.errors.any? %>
<div class="error_messages">
<h2><%= pluralize(#letter.errors.count, "error")%>stopped this message from being saved</h2>
<ul>
<% #letter.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
<% end %>
<div class="field">
<%= f.label :email %><br />
<%= f.text_field :email %>
</div>
<div class="field">
<%= f.label :subject %><br />
<%= f.text_field :subject %><br />
</div>
<div class="field">
<%= f.label :message, "Message" %><br />
<%= f.text_area :message, size: "60x10" %>
</div>
<div class="actions"><%= f.submit "Submit", class: "btn btn-small btn-primary" %></div>
<% end %>
The form is on the home page which is in the "Welcome Controller".
Any help would be great.
It looks like you build letter in action new when form is drawn on other view :)
You should move #letter = Letter.new to appropriate action
One of the variant is:
#WelcomeController
def home
#letter = Letter.new
end
#LettersController
def create
#letter = Letter.new(params[:letter])
if #letter.save
redirect_to letters_path, :notice => "Your letter was sent!"
else
render "welcome/home"
end
end
be careful if you prepare some data in action home you should care about initializing them for action create when validation failed because you render "welcome/home" view

Rails 4.0 & Cocoon: nested fields under fields_for don't appear in Edit

Hoping someone can suggest a fix here. I am fairly new to Rails, and exploring the changes in Rails 4.0. I built this simple recipe book app in Rails 4.0. I have a main model for recipes (name, cook_time, oven_temp, instructions, etc.). Because some recipes may have 5 ingredients and others may have 20, I wanted to break ingredients out in a separate model with a has_many association. So Recipes has_many Ingredients and accepts_nested_attributes_for :ingredients. Here are the models:
recipe.rb
class Recipe < ActiveRecord::Base
belongs_to :user
belongs_to :category
has_many :ingredients, :dependent => :destroy
validates :name, presence: true, length: { maximum: 50 }
accepts_nested_attributes_for :ingredients,
:reject_if => lambda { |a| a[:name].blank? },
:allow_destroy => true
end
ingredient.rb
class Ingredient < ActiveRecord::Base
belongs_to :recipe
end
Reminder: Rails 4.0 no longer uses attr_accessible, but moves assignment into the controller with strong params.
recipes_controller.rb
class RecipesController < ApplicationController
before_action :find_recipe, only: [:show, :edit, :update, :destroy]
before_action :set_current_user, except: [:index, :show]
respond_to :html, :js
...
def edit
end
def update
if #recipe.update_attributes(recipe_params)
redirect_to #recipe
else
render :edit
end
end
...
def find_recipe
#recipe = Recipe.find(params[:id])
end
private
def recipe_params
params.require(:recipe).permit( :id, :name, :category_id, :cook_time, :oven_temp, :calories, :instructions, :notes, :email, ingredients_attributes: [:id, :name])
end
end
I'm using the excellent Cocoon gem from nathanvda to manage nested form fields dynamically, and it works great in 'recipes#new', and saves the information correctly. However, the ingredients fields do not appear in the 'recipes#edit' view! Here is the code for my 'form' and ingredients_fields partials.
_form.html.erb (abbreviated)
<%= form_for #recipe, html: { class: 'form-horizontal' } do |f| %>
<fieldset>
<legend>Main Information</legend>
<div class="field form-group">
<%= f.label :name, "Recipe name", class: "col-lg-2 control-label" %>
<div class="col-lg-5">
<%= f.text_field :name, class: "form-control" %>
</div>
</div>
<div class="field form-group">
<%= f.label :cook_time, class: "col-lg-2 control-label" %>
<div class="col-lg-5">
<%= f.text_field :cook_time, class: "form-control" %>
</div>
</div>
...
</fieldset>
<legend>Ingredients</legend>
<p>Number of ingredients: <%= f.object.ingredients.count unless f.object.ingredients.nil? %></p>
<fieldset id="ingredients">
<% f.fields_for :ingredients do |builder| %>
<%= render 'ingredient_fields', :f => builder %>
<% end %>
<p class="links">
<%= link_to_add_association 'add ingredient', f, :ingredients, { class:"btn btn-primary" } %>
</p>
</fieldset>
<fieldset>
<legend>How to make it</legend>
<div class="field form-group">
<%= f.label :instructions, class: "col-lg-2 control-label" %>
<div class="col-lg-5">
<%= f.text_area :instructions, class: "form-control", rows: "7" %>
</div>
</div>
...
</div>
</fieldset>
<% end %>
_ingredients_fields.html.erb
<div class="nested-fields">
<div class="form-group">
<%= f.label :name, "Ingredient", class: "col-lg-2 control-label" %>
<div class="col-lg-5">
<%= f.text_field :name, class: "form-control" %>
</div>
<%= link_to_remove_association "remove", f %>
</div>
</div>
As I said, pretty simple, removed all error checking and messaging, just the basics. Any ideas about what I am missing? I know there are ingredients in the recipe as I load edit from a show view. Thanks in advance for any advice!
Kevin
Ack. it was as simple as an '=' sign - must have looked at the <% f.fields_for... %> a thousand times and just didn't notice I'd missed adding the <%=.
<%= f.fields_for :ingredients do |builder| %>