使用jquery-repeater渲染PHP SQL结果实现嵌套增删表单的问题
嵌套编码分组增删实现方案
不需要强制适配jquery-repeater,直接用Twig渲染基础结构+原生/jQuery事件委托的方式实现,完全规避插件嵌套配置的结构错误问题,逻辑可控改造成本低。
第一步:后端数据预处理
SQL查询出结果后先整理成分组结构,忽略不需要处理的productcode字段,直接按id升序排序后传给Twig,结构参考:
// 处理后传入模板的数组示例 $codeGroups = [ [ 'id' => 203, 'main' => ['code' => '对应主编码值', 'type' => 'Main'], 'subs' => [ ['code' => '子编码1值', 'type' => 'Sub'], ['code' => '子编码2值', 'type' => 'Sub'] ] ], [ 'id' => 204, 'main' => ['code' => '对应主编码值', 'type' => 'Main'], 'subs' => [ ['code' => '子编码A值', 'type' => 'Sub'] ] ] ];
第二步:Twig模板结构编写
直接按两层容器嵌套写结构,不要套错层级:
{# 最外层:所有主分组容器 #} <div class="main-group-container"> {% for group in codeGroups %} <div class="main-group-block" data-group-id="{{ group.id }}"> {# 主编码行 #} <div class="main-code-row"> <input type="text" class="main-code-input" name="groups[{{ group.id }}][main][code]" value="{{ group.main.code }}"> <button type="button" class="btn-delete-main">删除主编码</button> </div> {# 子编码容器 #} <div class="sub-group-container" data-group-id="{{ group.id }}"> {% for sub in group.subs %} <div class="sub-code-row"> <input type="text" class="sub-code-input" name="groups[{{ group.id }}][subs][{{ loop.index }}][code]" value="{{ sub.code }}"> <button type="button" class="btn-delete-sub">删除子项</button> </div> {% endfor %} </div> <button type="button" class="btn-add-sub">Add Sub Repeater</button> </div> {% endfor %} </div> <button type="button" id="btn-add-main">Add MAIN REPEATER</button>
第三步:交互逻辑实现
用事件委托方式绑定所有操作,支持动态新增元素的事件响应,jQuery示例如下,原生JS逻辑一致:
$(function () { // 删除主编码:连带移除整个分组(含所有子项) $(document).on('click', '.btn-delete-main', function () { $(this).closest('.main-group-block').remove(); }); // 删除单个子编码:仅移除当前子项 $(document).on('click', '.btn-delete-sub', function () { $(this).closest('.sub-code-row').remove(); }); // 为当前分组新增子编码输入框 $(document).on('click', '.btn-add-sub', function () { const $currentGroup = $(this).closest('.main-group-block'); const groupId = $currentGroup.data('group-id'); const tempIndex = Date.now(); // 用时间戳生成临时索引避免name冲突 const subTpl = ` <div class="sub-code-row"> <input type="text" class="sub-code-input" name="groups[${groupId}][subs][${tempIndex}][code]" value=""> <button type="button" class="btn-delete-sub">删除子项</button> </div> `; $currentGroup.find('.sub-group-container').append(subTpl); }); // 新增完整主+子编码分组 $('#btn-add-main').on('click', function () { const newGroupId = 'new_' + Date.now(); // 新增分组用临时id,提交后后端做持久化 const newSubIndex = Date.now() + 1; const mainTpl = ` <div class="main-group-block" data-group-id="${newGroupId}"> <div class="main-code-row"> <input type="text" class="main-code-input" name="groups[${newGroupId}][main][code]" value=""> <button type="button" class="btn-delete-main">删除主编码</button> </div> <div class="sub-group-container" data-group-id="${newGroupId}"> <div class="sub-code-row"> <input type="text" class="sub-code-input" name="groups[${newGroupId}][subs][${newSubIndex}][code]" value=""> <button type="button" class="btn-delete-sub">删除子项</button> </div> </div> <button type="button" class="btn-add-sub">Add Sub Repeater</button> </div> `; $('.main-group-container').append(mainTpl); }); })
关键适配说明
- 渲染顺序:后端预处理数据时直接按id升序排列数组,Twig循环默认按数组顺序输出,自然满足先渲染id=203、再渲染id=204的要求
- 逻辑隔离:主、子项的删除操作通过
closest()方法向上查找对应层级的容器做移除,不会出现误删其他分组内容的问题 - 事件兼容:所有动态生成的按钮都通过委托方式绑定事件,不会出现新增元素点击无响应的问题
- 字段兼容:新增数据用时间戳生成临时标识,不会和已有数据的name属性冲突,后端提交时可直接通过id判断是更新还是新增
- 冗余字段处理:模板中完全不输出
productcode相关表单项,不需要额外做字段过滤,不影响原有逻辑
该方案不需要依赖第三方重复器插件,所有结构和逻辑完全可控,不会出现jquery-repeater常见的嵌套层级匹配、动态新增字段name错乱的问题,后续调整样式或新增交互不需要翻阅插件文档。
内容的提问来源于stack exchange,提问作者cpat431
相关产品推荐
相关产品推荐

