Phoenix使用inputs_for生成动态表单时未生成唯一表单元素问题
问题原因
该参数重复问题不是inputs_for的预期行为,是代码写法错误导致的:模板外层额外遍历了@items列表,每次循环都单独调用inputs_for @f, :items,而inputs_for每次调用都会默认从索引0开始生成表单项,最终所有输入框的name属性都对应索引0,后端自然只能识别到一个参数。
正确实现方案
inputs_for本身就支持自动遍历关联的所有嵌套数据,不需要手动套循环,按以下步骤调整即可:
- 首先确保构建changeset时已经预加载了items关联:
# Controller 层示例 shopping_cart = Repo.get(ShoppingCart, cart_id) |> Repo.preload(:items) changeset = ShoppingCart.changeset(shopping_cart, %{})
- 调整
_form.html模板代码,去掉外层的for循环,直接使用inputs_for遍历关联项:
<%= inputs_for @f, :items, fn sci -> %> <%# sci.data 就是当前遍历到的item对象,直接取对应字段即可 %> <%= number_input sci, :quantity, value: sci.data.quantity %> <% end %>
按上述写法生成的输入框name会自动按索引递增,格式为shopping_cart[items][0][quantity]、shopping_cart[items][1][quantity],后端拿到的参数会自动包含所有item数据。
自定义遍历列表的场景
如果确实需要使用自定义的@items列表(比如过滤、排序后的结果),不需要手动循环,直接给inputs_for传入collection参数指定遍历对象即可:
<%= inputs_for @f, :items, [collection: @items], fn sci -> %> <%= number_input sci, :quantity, value: sci.data.quantity %> <% end %>
用item id作为参数索引的可选方案
如果希望参数里的索引直接用item的主键id而非自增数字,只需要在ShoppingCart的changeset里把关联items的:id加入允许字段即可,无需前端手动修改name属性,参数提交后Phoenix会自动识别对应id的记录进行更新。
之前用JS修改name的方案确实存在隐患,比如参数结构和后端期望不匹配、后续新增表单项时需要同步修改JS逻辑,用原生的inputs_for能力即可完全覆盖需求。
内容的提问来源于stack exchange,提问作者tblev
相关产品推荐
相关产品推荐

