You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Select动态切换Radio Button(Ruby on Rails)

解决Rails表单中根据Horizon下拉选项动态更新Due Date单选按钮的问题

嘿,作为Rails新手碰到这种动态表单的需求确实容易卡壳,我来一步步帮你实现这个功能!

首先,咱们得先给Horizon的下拉选单加上标识,方便JavaScript监听它的变化,然后再写逻辑来更新单选按钮的文本和日期值。

第一步:修改表单中的下拉选单和单选按钮结构

先给select标签加个ID,方便后续JS获取它的值,同时给单选按钮的标签加上对应的类,方便更新文本:

<%= form_with(model: @goal, url: goals_path, id: "new-goal-form", local: true) do |f| %>
  <p><%= @goal.errors[:date].first %></p>
  <div class="field">
    <label class="label">Title</label>
    <div class="control">
      <%= f.text_field :title, class: "input"%>
    </div>
    <p><%= @goal.errors[:title].first %></p>
  </div>
  <div class="field">
    <label class="label">Description</label>
    <div class="control">
      <%= f.text_area :description, class: "textarea"%>
    </div>
  </div>
  <div class="field">
    <label class="label">Horizon</label>
    <div class="control">
      <div class="select">
        <!-- 给select添加id,方便JS监听 -->
        <%= f.select :horizon, options_for_select({"Weekly" => "week", "Quarterly" => "quarter", "Yearly" => "year"}, @horizon), include_blank: true, id: "horizon-select" %>
      </div>
    </div>
  </div>
  <div class="field">
    <label class="label">Due Date</label>
    <div class="control">
      <%= f.radio_button :date, DateTime.current.to_date.end_of_week.strftime('%Y-%m-%d'), :checked => true, id:"radio-this", class: "is-checkradio" %>
      <!-- 给标签加标识,方便更新文本 -->
      <label for="radio-this" id="this-period-label"> This <%= @horizon.present? ? @horizon.capitalize : "Week" %> </label>
      <%= f.radio_button :date, 1.week.from_now.to_date.end_of_week.strftime('%Y-%m-%d'), id:"radio-next", class: "is-checkradio" %>
      <label for="radio-next" id="next-period-label"> Next <%= @horizon.present? ? @horizon.capitalize : "Week" %> </label>
    </div>
  </div>
  <div class="field">
    <label class="label">Related goal</label>
    <div class="control">
      <div class="select">
        <%= f.select :related_goal_id, options_from_collection_for_select(@related_goal, 'id', 'title'), include_blank: true %>
      </div>
    </div>
  </div>
  <br/>
  <div class="field is-grouped">
    <div class="control">
      <%= f.submit "Save", class: "button is-primary" %>
    </div>
    <div class="control">
      <%= link_to "Cancel", goals_path(horizon: @horizon), class: "button" %>
    </div>
  </div>
<% end %>

第二步:添加JavaScript逻辑处理动态更新

接下来写JS代码,监听下拉选单的change事件,根据选中的Horizon计算对应的日期,然后更新单选按钮的value和标签文本。你可以把这段代码放在页面底部的<script>标签里,或者放到app/javascript/application.js中:

document.addEventListener('DOMContentLoaded', function() {
  const horizonSelect = document.getElementById('horizon-select');
  const thisRadio = document.getElementById('radio-this');
  const nextRadio = document.getElementById('radio-next');
  const thisLabel = document.getElementById('this-period-label');
  const nextLabel = document.getElementById('next-period-label');

  // 页面加载时,如果有默认选中的horizon,先初始化一次
  if (horizonSelect.value) {
    updateDueDateOptions(horizonSelect.value);
  }

  // 监听下拉选单的变化
  horizonSelect.addEventListener('change', function() {
    const selectedHorizon = this.value;
    if (selectedHorizon) {
      updateDueDateOptions(selectedHorizon);
    } else {
      // 选空选项时重置内容
      thisRadio.value = '';
      nextRadio.value = '';
      thisLabel.textContent = ' This ';
      nextLabel.textContent = ' Next ';
    }
  });

  // 核心函数:根据horizon计算日期并更新控件
  function updateDueDateOptions(horizon) {
    let thisPeriodEnd, nextPeriodEnd;
    const today = new Date();

    // 根据不同horizon计算周期结束日期
    switch(horizon) {
      case 'week':
        // 本周日(以周日为周结束)
        thisPeriodEnd = new Date(today.setDate(today.getDate() + (6 - today.getDay())));
        // 下周日
        nextPeriodEnd = new Date(thisPeriodEnd.setDate(thisPeriodEnd.getDate() + 7));
        break;
      case 'quarter':
        // 当前季度最后一天
        const quarter = Math.floor((today.getMonth() / 3)) + 1;
        thisPeriodEnd = new Date(today.getFullYear(), quarter * 3, 0);
        // 下一季度最后一天,跨年处理
        nextPeriodEnd = new Date(today.getFullYear(), (quarter + 1) * 3, 0);
        if (quarter === 4) {
          nextPeriodEnd.setFullYear(nextPeriodEnd.getFullYear() + 1);
        }
        break;
      case 'year':
        // 本年12月31日
        thisPeriodEnd = new Date(today.getFullYear(), 11, 31);
        // 下年12月31日
        nextPeriodEnd = new Date(today.getFullYear() + 1, 11, 31);
        break;
      default:
        return;
    }

    // 格式化日期为YYYY-MM-DD,适配Rails的日期字段
    const formatDate = (date) => {
      const year = date.getFullYear();
      const month = String(date.getMonth() + 1).padStart(2, '0');
      const day = String(date.getDate()).padStart(2, '0');
      return `${year}-${month}-${day}`;
    };

    // 更新单选按钮的value和标签文本
    thisRadio.value = formatDate(thisPeriodEnd);
    nextRadio.value = formatDate(nextPeriodEnd);
    const horizonText = horizon.charAt(0).toUpperCase() + horizon.slice(1);
    thisLabel.textContent = ` This ${horizonText}`;
    nextLabel.textContent = ` Next ${horizonText}`;
  }
});

为什么之前的onchange没效果?

大概率是你之前加的onchange事件没有正确绑定,或者对应的函数没有定义在全局作用域里。用addEventListener的方式更可靠,也符合现代JS的写法,能避免作用域问题。

现在你可以测试一下:切换Horizon下拉选项时,单选按钮的文本会跟着变化,对应的日期值也会更新成对应周期的结束日期啦!

内容的提问来源于stack exchange,提问作者fredjapan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 17:52:43