根据类别渲染字段 select rails

Render fields based on category select rails

所以我有以下形式,使用 cocoon 在部分

中嵌套一些字段
<%= form_with(model: tournament, local: true, class: "mt-8 md:mt-12") do |f| %>
  <% if tournament.errors.any? %>
    <div id="error_explanation" class="bg-red-100 text-red-700 rounded-md shadow-sm p-8">
      <h2 class="font-bold text-base"><%= pluralize(tournament.errors.count, "error") %> prohibited this tournament from being saved:</h2>

      <ul>
        <% tournament.errors.full_messages.each do |message| %>
          <li class="mt-1 font-semibold text-sm"><%= message %></li>
        <% end %>
      </ul>
    </div>
  <% end %>

  ....

  <div class="text-xl font-black mt-8">
    Tournament Standings
    <div class="border-2 border-indigo-600 bg-indigo-600 w-1/6 md:w-10 mt-1"></div>
  </div>

  <%= f.fields_for :tournament_standings do |tournament_standing| %>
    <%= render 'tournament_standing_fields', f: tournament_standing %>
  <% end %>

  <div class="mt-4 border-t border-gray-200 pt-5">
    <span class="mt-4 inline-flex rounded-md shadow-sm">
      <%= f.submit class: "mr-4 inline-flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-500 focus:outline-none focus:border-indigo-700 focus:shadow-outline-indigo active:bg-indigo-700 transition duration-150 ease-in-out" %>
      <%= link_to_add_association 'Add tournament standings', f, :tournament_standings, class: "inline-flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-500 focus:outline-none focus:border-indigo-700 focus:shadow-outline-indigo active:bg-indigo-700 transition duration-150 ease-in-out mr-2" %>
    </span>
  </div>
<% end %>

在该渲染中我有以下内容

<div class="mt-4">
  <%= f.label "Select tournament game", class: "block text-sm font-medium text-gray-700" %>
  <div class="mt-1 rounded-md shadow-sm">
    <%= f.select(:category_id, Category.all.map{|c| [c.name, c.id]}, {prompt: true}, { class: "ts_select block form-select w-full transition duration-150 ease-in-out sm:text-sm sm" }) %>
  </div>
</div>

<div class="nested-fields">
  <div class="mt-4">
    <%= f.label :team, class: "block text-sm font-medium text-gray-700" %>
    <div class="mt-1 rounded-md shadow-sm">
      <%= f.select(:team_id, Team.all.map{|t| [t.name, t.id]}, {prompt: "Select a team"}, { class: "block form-select w-full transition duration-150 ease-in-out sm:text-sm" }) %>
    </div>
  </div>

  <div class="flex flex-col md:flex-row">
    <div class="mt-4 md:mr-4">
      <div class="mt-1">
        <div class="flex rounded-md shadow-sm">
          <span class="inline-flex items-center px-3 rounded-l-md border border-r-0 border-gray-300 bg-gray-50 text-gray-500 sm:text-sm">
            Position
          </span>
          <%= f.number_field :position, class: "flex-1 form-input block w-full rounded-none rounded-r-md transition duration-150 ease-in-out sm:text-sm sm" %>
        </div>
      </div>
    </div>

    <%= render partial: "tournaments/fpartials/default", tournament: @tournament, locals: { f: f } %>
    <%= render partial: "tournaments/fpartials/rocket_league_fields", tournament: @tournament, locals: { f: f } %>

    <div class="mt-4 md:mr-4">
      <div class="mt-1">
        <div class="flex rounded-md shadow-sm">
          <span class="inline-flex items-center px-3 rounded-l-md border border-r-0 border-gray-300 bg-gray-50 text-gray-500 sm:text-sm">
            Assists
          </span>
          <%= f.number_field :assists, class: "flex-1 form-input block w-full rounded-none rounded-r-md transition duration-150 ease-in-out sm:text-sm sm" %>
        </div>
      </div>
    </div>

    <div class="mt-4">
      <div class="mt-1">
        <div class="flex rounded-md shadow-sm">
          <span class="inline-flex items-center px-3 rounded-l-md border border-r-0 border-gray-300 bg-gray-50 text-gray-500 sm:text-sm">
            Prize $
          </span>
          <%= f.number_field :prize, class: "flex-1 form-input block w-full rounded-none rounded-r-md transition duration-150 ease-in-out sm:text-sm sm" %>
        </div>
      </div>
    </div>
  </div>

  <%= link_to_remove_association 'Delete tournament standing', f, class: "mt-4 inline-flex justify-center text-sm font-medium" %>
</div>

哪个有效,如您所见,我有两个渲染部分,我希望能够根据所选类别显示这些字段 category_id 默认值应该是默认显示的字段和隐藏的火箭联盟字段,但是,假设用户选择 rocket league 类别,则需要隐藏默认字段并显示火箭联盟字段。

任何对此的帮助都会很好地让它在部分范围内工作。

有两种解决方法:

  1. 在页面加载时呈现所有部分,并在 select 框更改时使用 Javascript 到 show/hide 适当的部分。
  2. 使用 AJAX 请求呈现正确的部分,然后使用 Javascript 将其动态放入 page/DOM。

如果您有大量可能的部分或它们非常大,则选项 2 更可取。但是,如果您只是在谈论在两件事之间切换,则选项 1 会更直接。

对于选项 1:

  1. 用不同的 ID 将您的部分内容包装在 <div> 中,以便您的 Javascript 可以访问它们。在上面的代码中,您最初似乎没有在 select 框中设置值,因此大概您会从 'default' 集开始显示:

    <div id="default-fields">
      <%= render partial: "tournaments/fpartials/default", tournament: @tournament, locals: { f: f } %>
    </div>
    <div id="rocket-fields" hidden>
      <%= render partial: "tournaments/fpartials/rocket_league_fields", tournament: @tournament, locals: { f: f } %>
    </div>
    

    (您当然也可以在渲染时有条件地设置 'hidden'。)

  2. 添加一个 Javascript 处理程序,只要 selected 值发生变化就会触发。 'unobtrusive' 方法是将 Javascript 事件侦听器添加到页面的 Javascript 包中(通常在 app/assets/javascript 中)。类似于:

    document.addEventListener('turbolinks:load', () => {
      const element = document.getElementById('tournament[category_id]');
      if (element) {
        $('#tournament[category_id]').change((e) => {
          if (e.target.value == 'whatever_rocket_id_is') {
            ('#default-fields').hide();
            ('#rocket-fields').show();
          }
          else {
            ('#default-fields').show();
            ('#rocket-fields').hide();
          }
        });
      }
    });
    

请注意:因为您希望在类别和显示的字段之间紧密耦合,所以您不应使用记录 ID 作为 select 选项中的键;它可以改变,你不想在你的 Javascript 中硬编码记录 ID。最好的办法可能是对键和值都使用类别名称。首先重新考虑紧密耦合可能也值得。