带有 jQ​​uery 的 Rails 中的动态下拉(选择框)菜单不可逆

Posted

技术标签:

【中文标题】带有 jQ​​uery 的 Rails 中的动态下拉(选择框)菜单不可逆【英文标题】:A dynamic dropdown (selectbox) menu in Rails with jQuery is not reversible 【发布时间】:2021-09-17 07:43:33 【问题描述】:

在由 Rails 6.1 控制的网站表单上,我想实现“动态”或级联下拉菜单,以便第二个下拉菜单中的选项根据第一个下拉菜单中的所选项目而有所不同。

具体来说,我有一个与CountryTown 模型相关联的Person 模型。关系是Personbelongs_toTown,其中belongs_toCountryhas_many 中。定义了方法Country#nameTown#name。在createPerson 的网站上的 表单中,用户首先从下拉菜单(选择框)中选择国家,然后选择城镇 在第二个下拉菜单中。

我基本上遵循了#88 Dynamic Select Menus (revised) 的过程,但在标准 jQuery 中重写了它,而不是在 Railcasts 中使用 Coffee。

简而言之,我使用 Rail 的 form.grouped_collection_select 辅助方法为城镇创建了一个选择框;生成的 html 中的部分包含许多OPTGROUP,每个都对应一个国家,其中有多个子城镇belongs_to。关联的 jQuery 脚本过滤第二个(即,Town)下拉菜单,将 Country 下拉菜单中的选定项目与每个 OPTGROUPLABEL 进行比较城镇下拉菜单(选择框)。

它有点工作,但有一个严重的缺陷。基本上,它在第一次点击时起作用。然而,一旦用户改变主意并重新选择不同的国家城镇的所有选项都会消失。换句话说,用户的第一选择是不可逆的。这是一个糟糕的界面。

如何解决这个问题,让用户的选择总是可逆的?

下面是表单视图(hrb.erb)和javascript jQuery代码中的相关部分。这里,person 是模型Person 的一个新实例。它使用 Rails 6.1.4、Ruby 3.0.1 和 jQuery 3.5.1 进行了测试。

erb.html 用于表单

<%= form_with(model: person, local: true) do |form| %>
  <div class="field">
    <%= form.label 'town_id.country_id', 'Country'%>
    <%= form.collection_select town_id.country_id', Country.all,
        :id, :name, include_blank: true %>
  </div>

  <div class="field">
    <%= form.label 'place.town_id' %>
    <%= form.grouped_collection_select 'place.town_id', Country.all,
        :towns, :name, :id, :name, include_blank: true %>
  </div>
<% end %>

Javascript jQuery

 var contsel = "#"+$.escapeSelector('person_place.town_id.country_id');
 $(contsel).change(function()
   var prefsel = "#"+$.escapeSelector('person_place.town_id');
   var contsel = "#"+$.escapeSelector('person_place.town_id.country_id');
   var country = $.escapeSelector($(contsel+' :selected').text());
   var towns = $(prefsel).html();
   var options = $(towns).filter("optgroup[label='"+country+"']").html();
   if (options) 
     $(prefsel).html(options);
    else 
     $(prefsel).empty();
   
 )

【问题讨论】:

【参考方案1】:

有很多方法可以使用 Web 应用程序框架来实现动态下拉菜单。至于使用 Rails 的方式, this question 的答案似乎总结得特别好。

我认为 OP 使用基于 OPTGROUP 标签的 jQuery 的方式有一些优点,如果无论如何都不是没有缺点的话,

    Rails 端编码最少, 即使禁用了 Javascript,用户也可以制定(并提交)有意义的选择(好吧,谁会?)。

OP 的 Javascript 代码使生成的 HTML 不可逆,因为它使用 filter() 方法破坏性地修改 HTML。在用户选择提示change()的第一个动作时,HTML被不可逆地修改。例如,一旦选择了 Country=Australia,就 Javascript 代码而言,除了澳大利亚以外的所有 城镇 的内容都将被删除。因此,由于任何后续操作都将基于修改后的 HTML,因此 Javascript 代码对任何其他国家/地区的城镇一无所知。

下面的 Javascript jQuery 代码应该可以解决这个问题,使用户的选择是可逆的。它基于 OP 的示例代码,前半部分相同,只是增加了两行。

$(function()
 var contsel = "#" + $.escapeSelector('person_place.town_id.country_id');
 var townsel = "#" + $.escapeSelector('person_place.town_id');
 $(townsel).parent().hide();
 $(contsel).change(function() 
   var townsel = "#" + $.escapeSelector('person_place.town_id');
   var contsel = "#" + $.escapeSelector('person_place.town_id.country_id');
   var country = $.escapeSelector($(contsel + ' :selected').text());
   var towns = $(townsel).html();
   var options = $(towns).filter("optgroup[label='" + country + "']").html();
   if (options) 
     $(townsel).parent().show();
     $(townsel).show();
     $(townsel + " optgroup").hide();
     $(townsel + " optgroup[label='" + country + "']").show();
     $(townsel).find('option:selected').prop("selected", false);
    else 
     $(townsel).hide();
     $(townsel).parent().hide();
   
 )
)

这里有几点需要注意:

    主要区别在于 hideshow,这取决于用户的选择,并且永远不会删除或修改 HTML 中的任何其他部分。 为了使操作可逆,这明确的show 部分可能已被隐藏。 当所选的国家/地区 em>没有 Towns EM>或NO 国家/ / em>已经隐藏了城镇的整个选择框已选中。change() 发生时,即当用户选择一个新的国家 时,用户之前的选择(如果存在)将被清除。如果没有这个,从技术上讲,用户可以使用相互矛盾的组合(例如 Country=Australia 和 Town=Nairobi)提交表单数据。

这是用于实验的示例代码的jsfiddle。完整代码也可在at Github Gist 获得。

【讨论】:

以上是关于带有 jQ​​uery 的 Rails 中的动态下拉(选择框)菜单不可逆的主要内容,如果未能解决你的问题,请参考以下文章

带有 jQ​​uery 悬停淡入淡出效果的表格

通过单击带有 jQ​​uery 的锚点来平滑滚动元素

带有 jQ​​uery Ajax 的 JWT 令牌

内部带有 jQ​​uery 对话框的表单不提交

IBM Worklight 6.0 应用程序 - 带有 jQ​​uery Mobile 的 Windows Phone 8 中的页面呈现问题

带有 jQ​​uery Ajax 调用的 MVC 无法正确绑定空数组/可枚举