我正在关注Railscast 88以创建动态依赖下拉菜单。 http://railscasts.com/episodes/88-dynamic-select-menus
我将这些下拉列表呈现在我正在以多模型形式使用的部分中。我正在使用的表单遵循Ryan Bates的Advanced Rails Recipes流程。因为我在部分内部渲染下拉列表,所以我不得不严格遵循Railscast代码。在上面提供的Railscast链接上,注释30-31和60-62解决了这些问题并提供了我使用的方法。
对于新记录,一切都很好。我从下拉列表中选择一个父对象,javascript动态地将子选项限制为仅与我选择的父项相关联的项。我能够保存我的选择,一切都很好。
问题在于,当我返回编辑页面并单击子选择下拉列表时,将其绑定到父对象的约束不再存在。我现在能够选择任何孩子,无论孩子是否与父母有关。这是一个主要的用户体验问题,因为子对象列表太长而且复杂。我需要子选项始终依赖于所选的父级。
这是我的代码:
Controller#javascripts
def dynamic_varieties
@varieties = Variety.find(:all)
respond_to do |format|
format.js
end
end
观看#javascripts#dynamic_varieties.js.erb
var varieties = new Array();
<% for variety in @varieties -%>
varieties.push(new Array(<%= variety.product_id %>, '<%=h variety.name %>', <%= variety.id %>));
<% end -%>
function collectionSelected(e) {
product_id = e.getValue();
options = e.next(1).options;
options.length = 1;
varieties.each(function(variety) {
if (variety[0] == product_id) {
options[options.length] = new Option(variety[1], variety[2]);
}
});
}
观看#users#edit.html.erb
<% javascript 'dynamic_varieties' %>
<%= render :partial => 'form' %>
查看#users#_form.html.erb
<%= add_season_link "+ Add another product" %>
<%= render :partial => 'season', :collection => @user.seasons %>
查看#users#_season.html.erb
<div class="season">
<% new_or_existing = season.new_record? ? 'new' : 'existing' %>
<% prefix = "user[#{new_or_existing}_season_attributes][]" %>
<% fields_for prefix, season do |season_form| -%>
<%= error_messages_for :season, :object => season %>
<div class="each">
<p class="drop">
<label for = "user_product_id">Product:</label> <%= season_form.collection_select :product_id, Product.find(:all), :id, :name, {:prompt => "Select Product"}, {:onchange => "collectionSelected(this);"} %>
<label for="user_variety_id">Variety:</label>
<%= season_form.collection_select :variety_id, Variety.find(:all), :id, :name, :prompt => "Select Variety" %>
</p>
<p class="removeMarket">
<%= link_to_function "- Remove Product", "if(confirm('Are you sure you want to delete this product?')) $(this).up('.season').remove()" %>
</p>
</div>
<% end -%>
答案 0 :(得分:2)
这是你的罪魁祸首:
<%= season_form.collection_select :variety_id, Variety.find(:all),
:id, :name, :prompt => "Select Variety" %>
完美地在新记录上工作,因为它显示了所有内容,并且当选择在另一个选择框上发生更改时会被覆盖。
你需要做这样的事情:
<% varieties = season.product ? season.product.varieties : Variety.all %>
<%= season_form.select :variety_id,
options_from_collection_for_select(varieties, :id,
:name, season.variety_id), :prompt => "Select Variety" %>
仅使用与season.product关联的品种。如果season.product不存在,则列出所有这些。如果现有记录具有variety_id,它还将自动选择正确的记录。
改变也不会有害。
<%= season_form.collection_select :product_id, Product.find(:all),
:id, :name, {:prompt => "Select Product"},
{:onchange => "collectionSelected(this);"} %>
到
<%= season_form.select :product_id,
options_from_collection_for_select(Product.find(:all),
:id, :name, season.product), {:prompt => "Select Product"},
{:onchange => "collectionSelected(this);"} %>
将在页面加载时选择正确的产品。第二部分基本上就是执行BYK第一个建议的Rails方式。但是,考虑到赋予选择框的onchange方法的性质,这条线本身不能解决问题。它只会通过突出显示与季节相关的产品来增强用户体验。
答案 1 :(得分:1)
我认为你有两个选择:
并且注意:永远不要相信JavaScript强制用户将适当的数据发送到服务器。