RoR edit value of join table from nested form - ruby-on-rails

I am a noob on Ror. Been looking for my problem answers for 3 days now, I have been looking for the answers but can’t find one with my specific problem. ( I even found it hard to write the right title)
So I have been trying to build a nested form in RoR. I have a simple order form that enable users to specify the quantity they ordered in that form. The form will only store the value into the database if the quantity text field is not empty.
The Order form is simply look like this:
I am storing the quantity data into the join table between order and inventory which has many to many relationship through Inventory_orders table. now in the Inventory_orders table instead of only having orders_id and inventories_id , I also add the quantity column.
now I have been able to get the form working with the code below:
Controller:
def new
#order = Order.new
#customer = Customer.all
#inventories_list = Inventory.all
#inventory_order = #order.inventory_orders.build
end
def create
#order = Order.new(order_params)
#inventories_list = Inventory.all #controller can call any model
respond_to do |format|
if #order.save
format.html { redirect_to #order, notice: 'Order was successfully created.' }
format.json { render :show, status: :created, location: #order }
else
format.html { render :new }
format.json { render json: #order.errors, status: :unprocessable_entity }
end
end
end
def order_params
params.require(:order).permit(:customer_id, :order_ids => [],:inventory_orders_attributes => [:id, :quantity, :inventory_id ])
end
View:
<%= form_for(#order) do |f| %>
<div id = “Main_Container">
*** Some Code ***
<table id = "inventory_table">
<tr>
<td class = "prodCodeTitle"><h3>Prod Code</h3></td>
<td class = "prodResult"><h3>Quantity</h3></td>
<td class = "prodResult"><h3>Size</h3></td>
<td class = "prodResult"><h3>Price</h3></td>
</tr>
//Here display all the inventories list
<% #inventories_list.each do |t| %>
<tr>
<td class ="prodResult"><%= link_to t.pName, inventory_path(t), :remote => true %></td>
<td class = “prodResult">
//nested form for the join table
<%= f.fields_for :inventory_orders do |qty| %>
<%= qty.hidden_field :inventory_id , value: t.id %>
<%= qty.number_field :quantity %>
<%end%>
</td>
<td class = "prodResult"><%= t.pMeter %></td>
<td class = "prodResult"><%= t.pSellPrice %></td>
</tr>
<% end %>
*** Some Code***
<% end %>
Model:
class Order < ActiveRecord::Base
belongs_to :customer
has_many :inventory_orders
has_many :inventories, through: :inventory_orders
validates :customer_id, :presence => true
accepts_nested_attributes_for :inventory_orders, :reject_if => lambda { |a| a[:quantity].blank?}
end
class InventoryOrder < ActiveRecord::Base
belongs_to :inventory
belongs_to :order
validates :quantity, :presence => true
end
Now when creating new Orders form , the application works and store the data that I want in the inventory_orders table.
The problem occurs when I try to edit the form. When trying to click on edit button I get this output in my View file:
for example this is what I Input into the form:
when I try to edit the form this is what I get:
this is my controller for edit:
def edit
#order = Order.find(params[:id])
#customer = Customer.all
#inventories_list = Inventory.all
end
I have been looking at the psql database schema by manual do sql query as follow:
select * from inventory_orders where inventory_orders.order_id = 63;
and get this as result:
now it seems that the fields_for Inventory_orders get the number of rows returned , but I don't get why all the quantity also get displayed 4 times for each product. Also how can I ensure that when I try to edit quantity for product “aaa” it will only display one number_field with what users has input before.
Sorry for the long post,otherwise I am not sure how to clearly convey my meaning.
EDITED
this to show my Inventory Model:
Class Inventory < ActiveRecord::Base
has_many :inventory_orders
has_many :orders, through: :inventory_orders
end

You need to use the following:
//Here display all the inventories list
<% #inventories_list.each do |t| %>
<%= link_to t.pName, inventory_path(t), :remote => true %>
<%= f.fields_for :inventory_orders, t do |qty| %>
<%= qty.hidden_field :inventory_id , value: t.id %>
<%= qty.number_field :quantity %>
<% end %>
<%= t.pMeter %>
<%= t.pSellPrice %
<% end %>
The issue is that since f.fields_for populates a form based on the built associated objects, if you're passing 4 fully constructed objects through the edit action, fields_for is going to populate all of them each time.
What you need is to use the instance of the associated data.
I think your code could be improved a lot:
#app/controllers/orders_controller.rb
class OrdersController < ApplicationController
def new
#order = Order.new
#inventory = Inventory.all
#inventory_order = #order.inventory_orders.build
end
def edit
#order = Order.find params[:id]
#inventory = Inventory.all
end
end
#app/views/orders/new.html.erb
<%= form_for #order do |f| %>
<% #inventory.each do |inventory| %>
<%= f.fields_for :inventory_orders, item do |item| %>
<%= item.text_field :quantity %>
<% end %>
<% end %>
<%= f.submit %>
<% end %>
#app/views/orders/edit.html.erb
<%= form_for #order do |f| %>
<%= f.fields_for #order.inventory_orders do |item| %>
<%= item.text_field :quantity %>
<% end %>
<%= f.submit %>
<% end %>

Related

data not saved in nested table with hidden (array) field passed

when I submit the form :
Parameters: {"authenticity_token"=>"LJ/ZME2lHZ7VwCDgPKX6OFe326fXSXo5UB4M0cPwbCE=", "project_id"=>"second", "utf8"=>"✓", "commit"=>"Add Todo", "esthour"=>{"rfp_id"=>"2", "cms_est_hours"=>"", "modul1hours_attributes"=>{"0"=>{"module_est_hours"=>"11", "modul1_id"=>"3"}, "1"=>{"module_est_hours"=>"111", "modul1_id"=>"4"}}, "designpages_est_hours"=>"", "ecommerce_est_hours"=>""}}
models
class Esthour < ActiveRecord::Base
has_many :modul1hours
accepts_nested_attributes_for :modul1hours
end
class Modul1hour < ActiveRecord::Base
belongs_to :esthour
attr_accessible :module_est_hours,:module_act_hours,:modul1_id,:esthour_id
end
view
<% #m1.map(&:id).each do |id|%>
<%= b.fields_for :modul1hours, #esthour.modul1hours.build do |f| %>
<%= f.hidden_field :modul1_id, :value => id %>
<%= f.text_field :module_est_hours, :size => 30 %>
</tr>
<% end %>
<% end %>
controller
def new
#esthour = Esthour.new
#project = params[:project_id]
respond_to do |format|
format.html # new.html.erb
format.json { render :json => #esthour }
end
end
You can see my earlier question regarding this.
I'm waiting for valuable reply. Thanks.
you should add in your Esthour model:
attr_accessible :modul1hours_attributes

data not saved in nested table with hidden field(array)passed

when I submit the form :
Parameters: {"authenticity_token"=>"LJ/ZME2lHZ7VwCDgPKX6OFe326fXSXo5UB4M0cPwbCE=", "project_id"=>"second", "utf8"=>"✓", "commit"=>"Add Todo", "esthour"=>{"rfp_id"=>"2", "cms_est_hours"=>"", "modul1hours_attributes"=>{"0"=>{"module_est_hours"=>"11", "modul1_id"=>"3"}, "1"=>{"module_est_hours"=>"111", "modul1_id"=>"4"}}, "designpages_est_hours"=>"", "ecommerce_est_hours"=>""}}
models
class Esthour < ActiveRecord::Base
has_many :modul1hours
accepts_nested_attributes_for :modul1hours
end
class Modul1hour < ActiveRecord::Base
belongs_to :esthour
attr_accessible :module_est_hours,:module_act_hours,:modul1_id,:esthour_id
end
view
<% #m1.map(&:id).each do |id|%>
<%= b.fields_for :modul1hours, #esthour.modul1hours.build do |f| %>
<%= f.hidden_field :modul1_id, :value => id %>
<%= f.text_field :module_est_hours, :size => 30 %>
</tr>
<% end %>
<% end %>
controller
def new
#esthour = Esthour.new
#project = params[:project_id]
respond_to do |format|
format.html # new.html.erb
format.json { render :json => #esthour }
end
end
You can see my earlier question regarding this.
I'm waiting for valuable reply. Thanks.

pass array hidden field in nested model rails

I have following code in my view:
<% #m1.map(&:id).each do |id|%>
<%= b.fields_for :modul1hours do |f| %>
<%= f.hidden_field :modul1_id, id %>
<%= f.text_field :module_est_hours, :size => 30 %>
</tr>
<% end %>
<%end%>
params passing in console
Parameters: {"authenticity_token"=>"LJ/ZME2lHZ7VwCDgPKX6OFe326fXSXo5UB4M0cPwbCE=", "esthour"=>{"rfp_id"=>"6", "ecommerce_est_hours"=>"", "modul1hours"=>{"module_est_hours"=>"3"}, "designpages_est_hours"=>"", "cms_est_hours"=>""}, "modul1_ids"=>["12", "13", "14"], "utf8"=>"✓", "project_id"=>"second", "commit"=>"Add Todo"}
Current user: admin (id=1)
modul1_ids is the hidden array based on that three text box is created but when i submit the page gives me:
ActionView::Template::Error (undefined method `merge' for 12:Fixnum):
in first textbox i passed 1
second 2
and in third 3
last value(3) isthe s passing that one can see in the console params module_est_hours"=>"3, but what about rest two fields y not passing and whats the solution for an error. Please help me.
Edit 1
<% #m1.map(&:id).each do |id|%>
<%= b.fields_for :modul1hours do |f| %>
<%= hidden_field_tag "modul1_ids[]", id %>
<%= f.text_field :module_est_hours, :size => 30 %>
</tr>
<% end %>
<%end%>
this code does not give the error, but also value is not stored in modul1hours table
The field of the modul1hours table are:
integer :modul1_id
decimal :module_est_hours
decimal :module_act_hours
integer :esthours_id
]
.rb
belongs_to :esthour
attr_accessible :module_est_hours,:module_act_hours
and controller
Update
def new
#esthour = Esthour.new
#gg = #esthour.modul1hours.build
#project = params[:project_id]
#rfp = params[:rfp_id]
#m1 = Modul1.where(:rfp_id => #rfp.id)
respond_to do |format|
format.html # new.html.erb
format.json { render :json => #esthour }
end
end
over Update
# GET /project_todos/1/edit
def edit
#esthour = Esthour.find(params[:id])
end
def create
#project = params[:project_id]
#esthour = Esthour.new(params[:esthour])
user_params = params.select{|k,v| k.include?('esthour')}
respond_to do |format|
if #esthour.save
get_issue_attribute_param1(user_params)
format.html { redirect_to project_rfp_url(#project,#esthour.rfp_id), :notice => 'hours was successfully created.' }
format.json { render :json => #esthour, :status => :created, :location => #esthour }
else
format.html { render :action => "new" }
format.json { render :json => #esthour.errors, :status => :unprocessable_entity }
end
end
end
is there any build needed?eg Esthour.modul1hour.build in new def of controller coz record not saved in table?
view
<%= form_for #esthour,:rfp_id => #rfp.id,:project_id => #project do |b| %>
<%= b.hidden_field :rfp_id, :value => #rfp.id %>
<%= hidden_field_tag :project_id, #project %>
<table>
<tr> <td><b>Menutype </b></td>
<% if #rfp.menutype.present? %>
<td><%= #rfp.menutype %></td>
<td><%= b.number_field :menutype_est_hours %></td>
<% end %>
</tr>
<tr> <td> <b>Number of menu</b> </td>
<% if #rfp.numberofmenu.present? %>
<td><%= #rfp.numberofmenu %></td>
<td><%= b.number_field :numberofmenu_est_hours %></td>
<% end %>
</tr>
<tr>
<% #m1.map(&:id).each do |id|%>
<%= b.fields_for :modul1hours do |f| %>
<%= f.hidden_field :modul1_id, value => id %>
<%= f.text_field :module_est_hours, :size => 30 %>
</tr>
<% end %>
<% end %>
</table>
<%= b.submit 'Add Todo' %>
<% end %>
#esthour = Esthour.new
#gg = #esthour.modul1hours.build
#project = params[:project_id]
In this line:
<%= f.hidden_field :modul1_id, id %>
You are saying that you want the hidden field binded with modul1hour modul1_id method and options being id. Second parameter for FormBuilder hidden_field is expected to be a hash (which is then merged against default options). To do what you want do:
<%= f.hidden_field :modul1_id, value: id %>
Hidden fields aren't really the issue here
Apart from #BroiStatse's answer, I can see the issue as how you handle the params on your controller
Nested Models
Sending data to a controller sends that data to the relevant models. This is normally handled with accepts_nested_attributes_for, but can be handled manually too
From your controller code, I can't see how you're dealing with your extra data, but your error is caused by the incorrect merge of the params
Instead of saving the data manually, I would use the accepts_nested_attributes_for to save the data, like this:
#app/models/project.rb
Class Project < ActiveRecord::Base
accepts_nested_attributes_for :modul1hours
end
This will pass the params to your modul1hours model, where you'll then have to capture them with the relevant attr_accessible actions
f.fields_for
In order to get accepts_nested_attributes_for working properly, you have to ensure you use the f.fields_for function correctly.
You have to first build the ActiveRecord objects in your new controller action, like this:
def new
#project = Project.new
#project.modul1hours.build
end
Your problem is that you're then cycling through the ID's of your modul1hours model, yielding the f.fields_for artificially. Rails will only output an f.fields_for if the ActiveRecord object has been built in the controller:
"30" %>
This RailsCast gives you a better idea about this
What I would do is this:
#app/controllers/projects_controller.rb
def new
#project = Project.new
#m1.map(&:id).each do |i|
#project.modul1hours.build
end
end
#app/views/projects/new.html.erb
<%= b.fields_for :modul1hours do |f| %>
<%= hidden_field_tag :id, value :id %>
<%= f.text_field :module_est_hours, :size => "30" %>
<% end %>
I'm still thinking about how I would assign the ID's to the hidden field
Update
Try this:
#app/controllers/projects_controller.rb
def new
#project = Project.new
#project.modul1hours.build
end
Replace modul1hours with whatever your projects has_many of

Storing Array of Checkbox Selections

I have an attribute called "features" in my application. In my form, "features" consists of a list of check boxes. The idea here is that users can check off which "features" apply to their post, and that list of features gets saved into the record.
I see the array being saved in my console ("features"=>{"Private bathroom"=>"1", "Elevator"=>"0", "Complimentary breakfast"=>"1", "Great view"=>"1", "Cable TV"=>"0", "Fireplace"=>"0", "Other (see description)"=>"0", "Sweet location"=>"0"}).
However... When I view the record, features returns nil. It doesn't seem to be saving the features array.
Code provided below. Any idea what I'm doing wrong here?
models/accommodation.rb
class Accommodation < ActiveRecord::Base
validates_presence_of :title, :description, :thing, :location
attr_accessible :photo_attributes, :title, :description, :thing, :borough, :location, :spaces, :price, :features
has_one :photo
has_many :requests
belongs_to :user
accepts_nested_attributes_for :photo, :allow_destroy => true
end
controllers/accommodation_controller.rb
class AccommodationsController < ApplicationController
before_filter :auth, :except => :show
uses_tiny_mce ( :options => {
:theme => 'advanced',
:theme_advanced_toolbar_location => 'top',
:theme_advanced_toolbar_align => 'left',
:theme_advanced_buttons1 => 'bold,italic,underline,image,bullist,numlist,separator,undo,redo',
:theme_advanced_buttons2 => '',
:theme_advanced_buttons3 => ''
})
def show
#accommodation = Accommodation.find(params[:id])
end
def new
#accommodation = current_user.accommodations.build
#accommodation.build_photo
end
def create
#accommodation = current_user.accommodations.build(params[:accommodation])
if #accommodation.save
flash[:notice] = "Successfully created your accommodation."
redirect_to #accommodation
else
render :new
end
end
def edit
#accommodation = Accommodation.find(params[:id])
end
def update
#accommodation = Accommodation.find(params[:id])
if #accommodation.update_attributes(params[:accommodation])
flash[:notice] = "Successfully updated accommodation."
redirect_to #accommodation
else
render :edit
end
end
def destroy
#accommodation = Accommodation.find(params[:id])
#accommodation.destroy
flash[:notice] = "Successfully destroyed accommodation."
redirect_to :inkeep
end
private
def auth
if current_user
if params[:action] != 'new' && params[:action] != 'create'
#accommodation = Accommodation.find(params[:id])
if #accommodation.user_id != current_user.id
flash[:notice] = "You don't own this accommodation!"
render :action => 'show'
end
end
return true
else
flash[:error] = "Please login first."
redirect_to :controller => 'sessions', :action => 'new'
end
end
end
views/accommodations/_form.html.erb
<%= form_for #accommodation, :html => {:multipart => true} do |f| %>
<%= f.error_messages %>
<p>
Title<br />
<%= f.text_field :title, :size => 60 %>
</p>
<p>
Description<br />
<%= f.text_area :description, :rows => 17, :cols => 75, :class => "mceEditor" %>
</p>
[...snip...]
<p>
<i>Featuring...</i>
<%= fields_for :features do |feature_fields| %>
<table>
<tr>
<td><%= feature_fields.check_box 'Private bathroom' %> Private bathroom</td>
<td><%= feature_fields.check_box 'Cable TV' %> Cable TV</td>
<td><%= feature_fields.check_box 'Complimentary breakfast' %> Complimentary breakfast</td>
</tr>
<tr>
<td><%= feature_fields.check_box 'Elevator' %> Elevator</td>
<td><%= feature_fields.check_box 'Fireplace' %> Fireplace</td>
<td><%= feature_fields.check_box 'Great view' %> Great view</td>
</tr>
<tr>
<td><%= feature_fields.check_box 'Sweet location' %> Sweet location</td>
<td><%= feature_fields.check_box 'Other (see description)' %> Other (see description)</td>
</tr>
</table>
<% end %>
</p>
[...snip...]
<% end %>
First, is the features array inside of your the accommodation hash in the params hash?
Second, there is no db column type which accepts an array, so you need to put
serialize :features
in the model. This will store the array as yaml in the db. You can also specify the data type as an argument to serialize() (probably Array in this case), but it's not always necessary.
I add the same problem today, it appears the form isn't properly built in the view.
Indeed, take a closer look at your params: params[:features] is outside params[:accomodation]
I simply added at the beginning of my create action:
params[:accomodation][:features] = params[:features]
And it works properly
What about your model? Do you have attr_accessible or attr_protected calls in there?

Why is this render :partial line iterating my collection twice?

Given these relationships:
class Account < ActiveRecord::Base
has_many :employments
has_many :people, :through => :employments
accepts_nested_attributes_for :employments
end
class Employment < ActiveRecord::Base
belongs_to :account
belongs_To :person
end
I'm trying to list the employment records for an account:
<% form_for #account do |f| -%>
<% f.fields_for :employments do |e| -%>
<%= render :partial => 'employment', :collection => #account.employments, :locals => { :f => e } %>
<% end -%>
<% end -%>
I've verified that the employment table in #account contains two records, but I get four copies of the partial because it iterates employments twice:
Employment Load (1.0ms) SELECT * FROM [employments] WHERE ([employments].account_id = 1)
Person Load (1.3ms) SELECT * FROM [people] WHERE ([people].[id] = 2)
Rendered accounts/_employment (17.9ms)
Person Load (1.5ms) SELECT * FROM [people] WHERE ([people].[id] = 1)
Rendered accounts/_employment (5.1ms)
Rendered accounts/_employment (2.2ms)
Rendered accounts/_employment (2.1ms)
Can anybody explain why that would happen?
Here's some additional information:
The _employment.html.erb partial:
<div class="employment">
<span class="name"><%= link_to h(employment.person.name), person_path(employment.person) %></span>
<span class="role"><%=h employment.role %></span>
<span class="commands"><%= remove_child_link "Remove", f %></span>
</div>
remove_child_link is the only place I need to generate a form field at. It creates the _delete field for the record and wires up a remove link that changes the value to '1'. The 'role' property may also be editable, though. The important thing is I don't want all of the fields to be editable.
The accounts_controller actions for this view:
def edit
#account = Account.find(params[:id])
end
def update
#account = Account.find(params[:id])
respond_to do |format|
if #account.update_attributes(params[:account])
flash[:notice] = "#{#account.business_name} was successfully updated."
format.html { redirect_to #account }
else
format.html { render :action => "edit" }
end
end
end
Ben got me going in the right direction. Some runtime inspection reveals that the record is stored in the object variable (which I already knew, but in a different context). So I can rewrite the fields_for clause as:
<% form_for #account do |f| -%>
<% f.fields_for :employments do |e| -%>
<div class="employment">
<span class="name"><%= link_to h(e.object.person.name), person_path(e.object.person) %></span>
<span class="role"><%=h e.object.role %></span>
<span class="commands"><%= remove_child_link "Remove", e %></span>
</div>
<% end -%>
<% end -%>
Its rendering that partial for each field in the employments model - when really you want to do it once for each employment record. That is, remove the iteration over the fields_for:
<% form_for #account do |f| -%>
<%= render :partial => 'employment', :collection => #account.employments %>
<% end -%>
You are correct to use fields_for, but it will render what's inside of it for each employment, so remove the :collection parameter from render :partial. Instead, use nested forms by putting this in your Account model:
accepts_nested_attributes_for :employments
read more about nested forms here: http://ryandaigle.com/articles/2009/2/1/what-s-new-in-edge-rails-nested-attributes

Resources