2009-10-07 79 views
1

我正在关注Railscast 88以创建一个动态依赖下拉菜单。 http://railscasts.com/episodes/88-dynamic-select-menus动态生成选择菜单不在编辑视图中保持状态

我在多模式表单中使用的部分内容中呈现这些下拉列表。我使用的形式遵循Ryan Bates的Advanced Rails Recipes流程。因为我在部分内部渲染下拉列表,所以我不得不严格遵循Railscast代码。在上面提供的Railscast链接上,评论30-31和60-62解决了这些问题,并提供了我使用的方法。

对于新记录,一切都很好。我从下拉列表中选择一个父对象,并且JavaScript动态地将子选项限制为仅与那些与我选择的父对象关联的项目。我可以保存我的选择,一切都很好。

问题是,当我回到编辑页面,并且我点击了子选择下拉列表时,将它绑定到父对象的约束不再适用。我现在可以选择任何孩子,不管它是否连接到父母。这是一个主要的用户体验问题,因为子对象列表太长而且很复杂。我需要孩子的选择总是依赖于选择的父母。

这里是我的代码:

控制器#的JavaScript

def dynamic_varieties 
    @varieties = Variety.find(:all) 
    respond_to do |format| 
    format.js 
    end 
end 

浏览#的JavaScript#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]); 
    } 
    }); 
} 

浏览#用户#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 %> 

视图#用户#_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 -%> 

回答

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);"} %> 

这将选择在页面加载正确的产品。第二部分主要是Rails做BYK第一个建议的方式。然而,考虑到给选择框的onchange方法的性质,这条线本身并不能解决问题。它只会强调与季节相关的产品,从而增强用户体验。

+0

是的!你摇滚。一直试图解决这个问题一个月。 – MikeH 2009-10-28 04:49:49

+0

很好完成=)我认为我必须开始学习Ruby =) – BYK 2009-10-28 08:47:45

+0

所有这些都是Rails,它完成了大部分繁重的工作。 – EmFi 2009-10-28 17:13:47

1

我认为你有两个选择:

  1. 给一个的产品(或简单地说是产品列表的第一个元素)的“选定”属性,这将强制浏览器始终选择该属性。
  2. 以“产品列表选择框”作为参数,在“dom ready”或“window.onload”上触发“collectionSelected”功能。

并且注意:永远不要信任JavaScript来强制用户发送正确的数据到服务器。

+0

我是javascript的noob。不完全理解#2。你能再解释一下吗?谢谢!关于该说明,我的关注仅仅是用户体验。通过限制品种列表,它变得易于管理,避免选择与选定产品无关的品种时出现无意的错误。 – MikeH 2009-10-25 04:10:22

+0

嗯,我不知道Ruby =)我需要整页代码来建议你实现我在#2中试图说的一种有用的方法。可能是一个Ruby&JS的人会来,并在我之前解释=) – BYK 2009-10-25 12:30:48

+0

告诉我你需要看什么,我会修改我的问题。 – MikeH 2009-10-25 19:41:12