基于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
相关产品推荐
相关产品推荐

