Rails+JavaScript无gem实现同时创建父模型与动态数量子模型
核心问题说明
你当前直接克隆DOM的方式会导致所有Tape输入字段的name属性中的索引值完全相同,Rails解析嵌套参数时会把重复索引的参数互相覆盖,所以无论你新增多少个字段,最终只能保存最后一个子模型,按以下步骤修改即可实现动态数量子模型的同步创建:
步骤1:修改控制器代码
调整默认初始化逻辑,补充必填的嵌套参数校验:
def new @paper = Paper.new # 自定义默认显示的输入组数量,这里设为1组即可 1.times { @paper.tapes.build } end def create @paper = Paper.new(paper_params) @paper.user_id = current_user.id if @paper.save redirect_to action_list_paper_path(@paper.id) else render 'new' end end private # 必须补充嵌套参数的白名单配置 def paper_params params.require(:paper).permit(:content, tapes_attributes: [:id, :label, :_destroy]) end
步骤2:修改视图代码
替换gem专属的表单方法,修正重复ID问题,新增删除标记字段:
<!-- 把nested_form_for改为Rails原生的form_for,不需要依赖额外gem --> <%= form_for(@paper) do |f| %> <div class="container"> <%= stylesheet_link_tag 'papers', media: 'all', 'data-turbolinks-track': 'reload' %> <div class="paper_form"> <div class="col-md-6"> <%= f.label(:content, "Name") %> <%= f.text_area :content %> </div> </div> </div> <div class="tape_form"> <%= f.fields_for :tapes do |tape| %> <!-- 把原来的id改为class,避免克隆后出现重复ID --> <div class="input_pluralBox"> <div class="input_plural"> <div class="col-md-4"> <%= tape.label :label %> <%= tape.text_field :label %> <!-- 新增删除标记隐藏字段,支持提交时删除对应子项 --> <%= tape.hidden_field :_destroy, class: 'tape-destroy' %> </div> </div> <input type="button" value="+" class="add pluralBtn"> <input type="button" value="-" class="del pluralBtn"> </div> <% end %> </div> <!-- 补充提交按钮 --> <div class="form-submit"> <%= f.submit "保存" %> </div> <% end %>
步骤3:修改JS代码
克隆元素时动态更新索引值,保证每个字段的参数标识唯一:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <script type="text/javascript"> $(document).on("click", ".add", function() { const $parent = $(this).parent(); const $newBox = $parent.clone(true); // 用当前输入组总数作为新组的唯一索引 const newIndex = $('.input_pluralBox').length; // 批量更新新组内所有字段的name、id属性的索引值 $newBox.find('input, select, textarea').each(function() { const oldName = $(this).attr('name'); if (oldName) { const newName = oldName.replace(/\[tapes_attributes\]\[\d+\]/, `[tapes_attributes][${newIndex}]`); $(this).attr('name', newName); } const oldId = $(this).attr('id'); if (oldId) { const newId = oldId.replace(/_tapes_attributes_\d+_/, `_tapes_attributes_${newIndex}_`); $(this).attr('id', newId); } // 清空新输入框的原有值 $(this).val(''); }); $newBox.insertAfter($parent); }); $(document).on("click", ".del", function() { const $target = $(this).parent(); if ($('.input_pluralBox').length > 1) { // 新增的未保存子项直接移除即可 $target.remove(); // 如果是编辑页面要删除已存在的子项,把上面一行替换为下面两行即可 // $target.find('.tape-destroy').val('1'); // $target.hide(); } }); </script>
内容的提问来源于stack exchange,提问作者frankyorg
相关产品推荐
相关产品推荐

