问题
I created a combobox that show suppliers so when supplier is selected will update a div showing purchases , after showing purchases will be in combobox but in a nested form so when I select a purchase will show me the amount of the purchase selected.
The problem is that when I select a purchase only works in the first line and when I select another combobox line show the amount of the first combobox selected.
Here my tables
suppliers
|id| |name|
1 Supplier A
2 Supplier B
shopping_documents
|id| |supplier_id|
1 1
2 2
shopping_products
|id| |shopping_document_id| |qty| |purchase_product_id|
1 1 1 1
2 1 1 2
purchase_products
|id| |name| |price_sale|
1 XP 1000
2 VISTA 2000
supplier_products
|id| |supplier_id| |amount| |purchase_product_id
1 1 1000 1
2 1 2000 1
3 2 3000 2
Here is the controller /app/controller/purchase_product_controller.rb
class ShoppingDocumentController < ApplicationController
def new
@document = ShoppingDocument.new
@suppliers= Supplier.all
@products = SupplierProduct.find(:all,:conditions=>['supplier_id =? ',params[:suppliers] ],:group=>['purchase_product_id'])
@purchases= SupplierProduct.find(:all,:conditions=>['supplier_id =? ',params[:suppliers] ],:group=>['purchase_product_id'])
4.times do
shopping_product = @document.shopping_products.build
end
end
def create
@document = ShoppingDocument.new(params[:shopping_document])
if @document.save
flash[:notice] = "Successfully created document."
redirect_to :action=>"index"
else
render :action => 'new'
end
end
def update_supplier_div
@products = SupplierProduct.find(:all,:conditions=>['supplier_id =? ',params[:suppliers] ],:group=>['purchase_product_id'])
end
def update_product_div
@purchases= SupplierProduct.find(:all,:conditions=>['purchase_product_id =? ',params[:product] ],:group=>['purchase_product_id'])
end
end
Here models:
class ShoppingDocument < ActiveRecord::Base
has_many :shopping_products
accepts_nested_attributes_for :shopping_products ,:allow_destroy => true
end
class ShoppingProduct < ActiveRecord::Base
belongs_to :shopping_document
belongs_to :purchase_product
end
class PurchaseProduct < ActiveRecord::Base
has_many :shopping_products
has_many :supplier_products
end
class SupplierProduct < ActiveRecord::Base
belongs_to :purchase_product
end
Here is the view: /app/view/purchase_product/new.html.erb
<% form_for @document, :url => {:controller=>"shopping_document",:action=>'create'} do |f| %>
Name: <%= select_tag 'suppliers',"<option value=\"\">Select</option>"+options_for_select(@suppliers.collect {|t| [t.name,t.id]} ),:onchange=>remote_function(:url=>{:controller=>"shopping_document",:action=>"update_supplier_div"},:with=>"'suppliers=' + $('suppliers').value"),:name=>"shopping_document[supplier_id]" %>
<% f.fields_for :shopping_products do |builder| %>
<%= render "shopping_product_fields", :f => builder %>
<% end %>
<p><%= link_to_add_fields "Add Product", f, :shopping_products %></p>
<p><%= f.submit "Submit" %></p>
<% end %>
Here is the partial view: /app/view/shopping_document/_shopping_product_fields.html.erb
<div class="fields">
<table>
<tr><td><%= f.text_field :qty , :size=>"9" %></td>
<td><%= select_tag "product","<option value=\"\">Select</option>"+options_for_select(@products.map { |c| [c.amount, c.id] }),:onchange=>remote_function(:url=>{:controller=>"shopping_document",:action=>"update_product_div"},:before=>"load_close('loading_condition')",:success=>"load_off('loading_condition')",:with=>"'product=' + $('product').value"),:class=>"product" %></td>
<td><%= f.select :purchase_product_id,"<option value=\"\">Select</option>"+options_for_select(@purchases.map { |c| [c.amount, c.id] }), {}, :class => "helping" %></td>
<td><%= link_to_remove_fields image_tag("standard/delete.png",:border=>0,:size=>"14x14"), f %></td>
</tr>
</table>
</div>
Here is the view rjs : /app/view/shopping_document/_update_supplier_div.rjs
page.select('.product').each do |select_tag|
page.replace_html select_tag, "<option value=\"\">Select</option>"+options_for_select(@products.map{|c| [c.purchase_product.name, c.purchase_product_id]})
end
Here is the view rjs : /app/view/shopping_document/_update_product_div.rjs
page.select('.helping').each do |select_tag|
page.replace_html select_tag,"<option value=\"\">Select</option>"+options_for_select(@purchases.map { |c| [c.amount, c.id] })
end
Is not updating in other lines just updating in the first line.
Please somebody can help me with this issue?
回答1:
The problem is that you always send the same value to your controller action when updating products.
Look at this line from your /app/view/shopping_document/_shopping_product_fields.html.erb partial:
<%= select_tag "product",
"<option value=\"\">Select</option>"+
options_for_select(@products.map { |c| [c.amount, c.id] }),
:onchange=> remote_function(
:url= {:controller => "shopping_document", :action=>"update_product_div"},
:before => "load_close('loading_condition')",
:success => "load_off('loading_condition')",
:with => "'product=' + $('product').value"),
:class=>"product" %>
From what I remember, in prototype.js, when you use $("something")
, it will find the first element in your DOM tree with id = 'someting'
The fact you're using $('product')
in the with
clause makes it always send the select tag value of the first row to the server, which is wrong.
You should be using $(this)
instead to make sure it's the value of the current
select that is being sent. So you need to replace:
:with => "'product=' + $('product').value"
With this one:
:with => "'product=' + $(this).value"
Also another problem is that when you change the product, it changes all the prices at once, and you only want that product's price to change. So, you need to be more specific about what line to change in your rjs.
The way we are going to do that is by changing the rjs you return. This rjs will now just assign a javascript variable:
page.assign "prices_for_product", "<option value=\"\">Select</option>" + options_for_select(@purchases.map { |c| [c.amount, c.id] })
And now we have to create a new javascript method that will use the values in that variable and set it to the current element. For that you need to add the following method in the javascript file where you defined load_close
and load_off
:
function update_prices_for_product(prices_select_tag){
prices_select_tag.innerHTML = prices_for_product;
}
But we still need to call this method when the AJAX call succeeds so we're going to add it to the :success
parameter.
:success => "load_off('loading_condition'); update_prices_for_product($(this));"
There is also something that is really confusing me. You are sending the supplier_product_id
that comes from your nested form to your controller, and injecting it in your query as a purchase_product_id
, all that called on the SupplierProduct table. From what I can see on your data model that's not correct and I must admit I'm really confused. See this in your update_product_div
action:
SupplierProduct.find(:all,:conditions=>['purchase_product_id =? ',params[:product] ],:group=>['purchase_product_id'])
What you're looking for are all the purchases done for a product. That would be something more like:
PurchaseProduct.find(:all,:conditions=>['supplier_product_id =? ', params[:product]])
You're clearly missing the supplier_product_id
column on your purchase_products
table.
That's as much as I can do for you, let's hope someone else jumps in...
I must say that this is really not good practice and you should use unobstrusive javascript all the time and upgrade to jquery. Moreover, use a more up-to-date version of Rails because not many people can still help you with Rails 2.3...
The only thing good with this answer is that it might make it work...
来源:https://stackoverflow.com/questions/25275441/issue-updating-class-in-combobox-from-nested-form