我想实现基于模型的下拉列表,其中当选择一个下拉列表(模型)的值时,它为第二个下拉列表中的选择提供了基础。
因此,我要求使用第一个下拉列表中的 id 来为第二个下拉列表应用过滤器。
<div class="study-search">
<%= form_with(url: show_subjects_path(:site_id), method: :get, html: {style: 'font-size:16px'}) do |f| %>
<%= f.label 'Select Study' %>
<%= f.select :id, Study.all.collect {|study| [study.title, study.id] }, {:include_blank => "--Choose--"}%>
<%= f.label 'Select Site' %>
<%= f.select :site_id, Site.where(study_id: :id).collect {|site| [site.name, site.id] }, {:include_blank => "--Choose--"}%>
<div class="btn btn-sm", style="display: inline">
<%= f.submit "GO" %>
</div>
<% end %>
</div>
以下代码片段中使用的 :id 不是正确的方法。请建议正确的方法。
<%= f.select :site_id, Site.where(study_id: :id).collect {|site| [site.name, site.id] }, {:include_blank => "--Choose--"}%>
在此处输入图像描述
有多种方法可以解决具有不同复杂程度的此问题。我将从最不复杂(但可能笨拙)到最复杂的
开始前两个选项将进行整页刷新,这并不像听起来那么糟糕,特别是如果您使用的是Turbo Drive(以前称为Turbolinks)。
这两个选项的警告是,导航将始终是 Turbo
visit
导航,这意味着每次您都会在导航堆栈中创建一个新页面。您可以通过使用 Stimulus 覆盖表单提交并使用replace
操作发出 TurboGET
请求来克服此问题。有关此内容的更多信息,请参阅涡轮驱动文档。
选项 1:最简单的一个
如果用户在更改第一个选择框时按下Go
按钮,您那里的内容已经可以实现您的预期。您只需更改选择Site
,即可按表单发送的参数进行过滤:
<%= f.select :site_id, Site.where(study_id: params[:site_id]).collect {|site| [site.name, site.id] }, {:include_blank => "--Choose--"}%>
页面再次呈现后,你将看到一个新的选择框,其中包含筛选的数据。
这种方法非常笨拙,但它为您优化它奠定了良好的基础。
选项 2:在change
上自动发送表单
现在,如果您添加一些Javascript,您将能够检测到第一个select
何时更改,并在更改时提交表单。为此,我建议您使用Stimulus JS。
查看文档以了解有关刺激的更多信息,如果您尚未使用它,以便能够遵循以下内容
- 创建一个刺激 JS 控制器,以便在
select
更改时提交表单
import { Controller } from "stimulus"
export default class extends Controller {
submit() {
// add some code that submits a form. An easy way to do it is to use Rails UJS to do it but it's no longer recommended by the community since it'll be deprecated soon.
Rails.fire(this.element, 'submit');
}
}
- 将刺激控制器添加到窗体中。
请注意,controller
和action
关键字将添加到form_with
方法和第一种f.select
方法中。
<div class="study-search">
<%= form_with(url: show_subjects_path(:site_id), method: :get, data: {controller: 'form'}, html: {style: 'font-size:16px'}) do |f| %>
<%= f.label 'Select Study' %>
<%= f.select :id, Study.all.collect {|study| [study.title, study.id] }, {:include_blank => "--Choose--"}, { data: {action: 'form#submit' }%>
<%= f.label 'Select Site' %>
<%= f.select :site_id, Site.where(study_id: params[:site_id]).collect {|site| [site.name, site.id] }, {:include_blank => "--Choose--"}%>
<div class="btn btn-sm", style="display: inline">
<%= f.submit "GO" %>
</div>
<% end %>
</div>
这基本上将模仿用户在第一个选择更改时单击Go
按钮
选项 3:使用涡轮框架
如果您使用的是最新版本的rails,并且已经包含了hotwire-rails
宝石,您将能够利用Rails的所有新技术以及随之而来的HTML over the wire技术。
最新版本的 turbo 现在处理表单(与 Turbolinks 相反)。有关如何执行此操作的技术,请参阅涡轮框架文档。使用涡轮增压帧,您基本上正在做与选项 1 和 2 相同的事情,后者发送 GET 请求并返回整页作为响应,但不同的是,使用 Turbo 帧,您将能够有选择地替换部分 DOM。
我还不是涡轮增压车架的专家,所以我不会发布一个确切的例子。
选项 4:使用涡轮流
这是最复杂的选项,但就使用动作电缆设置环境而言,它仍然相对简单。Turbo Stream 所做的是允许您使用 5 个基本的 dom 操作来针对 DOM 的更新:append
、prepend
、replace
、update
和remove
。 这些操作将使用 Web 套接字连接从服务器流式传输回前端。
在您的情况下,您可能希望使用新的选项列表replace
或update
选择。
我还不是涡轮流方面的专家,所以我不会发布一个确切的例子。
选项 5:查看刺激反射
来自StimulusReflex的网站:
[刺激反射是]一种使用 Ruby on Rails 制作现代、反应式 Web 界面的新方法。 我们通过拦截用户交互并通过实时websocket将其传递给Rails来扩展Rails和Stimulus的功能。这些交互由更改应用程序状态的反射操作处理。当前页面将快速重新呈现,并使用 CableReady 将更改发送到客户端。然后,该页面将变形以反映新的应用程序状态。整个往返过程使我们能够在 20-30 毫秒内更新 UI,而不会闪烁或昂贵的页面加载。
使用刺激反射,您可以获得非常无缝的体验来执行这些类型的交互式页面,但它需要额外的依赖性。这绝对值得一试,但它也是您将引入代码库的新依赖项(假设您采用版本 7 中将包含 Hotwire 的新 Rails 默认值)
选项 6:100% 前端
使用Stimulus JS,您可以在页面加载时从所有站点加载所有选项,并仅使用javascript进行过滤。这是可行的,但它肯定更复杂,因为你需要编写更多的javascript,如果你有太多的网站要搜索,你的初始页面加载可能会很慢。我不建议使用此方法,因为它可能会给您的应用程序增加不必要的复杂性,但这是一种选择。
结语
不管你做什么,如果你想在用户更改前端的某些内容时提交表单(例如更改选择字段的值或在文本字段中键入某些内容,或悬停或任何其他可以在前端跟踪的操作),你需要编写一些 javascript。我强烈建议您查看StimulusJS
,这是Hotwire
的一部分,这是前端rails生态系统的最新成员,这将是在即将发布的Rails 7版本中构建应用程序的默认方式。
我希望这能给你一些指示。
一些额外的链接,以赶上所有关于热线的事情(刺激,涡轮等)
Go Rails在这个主题上有很棒的资源 https://www.youtube.com/results?search_query=go+rails+stimulus
https://www.youtube.com/watch?v=AdktV7r2BQk
https://www.youtube.com/watch?v=Q7uOPVfZ3Go