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

Superforms更新对象数组:提交后端丢失新增题目数据

问题分析与解决

你遇到的核心问题是:前端通过按钮更新$form.questions后UI显示正常,但表单提交时后端收不到新增的题目数据。这是因为表单提交的数据是从DOM元素中收集的,而非直接从$form对象读取——你只是更新了状态,但没有为新增的question生成对应的表单输入字段,所以这些数据不会被包含在提交的表单里。

解决方案

1. 渲染新增题目的表单字段

需要在模板中循环遍历$form.questions,为每个question生成对应的输入控件,并设置正确的name属性(Superforms会通过这个属性关联到$form的对应值)。

2. 修改前端代码示例

更新你的Svelte组件,添加题目渲染部分:

<script lang="ts">
  const { form, errors, constraints, enhance } = superForm<ExamSchema, any, any>(data.form);
</script>

<form enctype="multipart/form-data" method="POST" use:enhance>
    <!-- 新增题目按钮 -->
    <button
        type="button"
        onclick={() => {
            const newQuestion: ExamSchema['questions'][0] = {
                id: shortUUID.uuid(),
                type: 'abc',
                points: '0',
                question: '',
                answers: [],
            };
            $form.questions = [...$form.questions, newQuestion];
        }}
        class="toolbar-btn"
    >
        <Abc height="16px" width="16px"></Abc> 添加题目
    </button>

    <!-- 渲染所有题目 -->
    {#each $form.questions as question, index}
        <div class="question-container">
            <h4>题目 {index + 1}</h4>
            <!-- 题目类型 -->
            <input 
                type="text" 
                name={`questions[${index}].type`} 
                bind:value={question.type}
                placeholder="题目类型"
            />
            <!-- 题目分值 -->
            <input 
                type="text" 
                name={`questions[${index}].points`} 
                bind:value={question.points}
                placeholder="分值"
            />
            <!-- 题目内容 -->
            <textarea 
                name={`questions[${index}].question`} 
                bind:value={question.question}
                placeholder="题目内容"
            ></textarea>
            
            <!-- 如果有answers字段,也需要渲染对应的输入 -->
            {#each question.answers as answer, ansIndex}
                <input 
                    type="text" 
                    name={`questions[${index}].answers[${ansIndex}]`} 
                    bind:value={answer}
                    placeholder="选项"
                />
            {/each}
        </div>
    {/each}

    <button type="submit" class="button-primary save-exam">
        {$i18n.t('teacher_panel.exam_editor.submit')}
    </button>
</form>

关键说明

  • 每个表单字段的name属性必须遵循questions[索引].字段名的格式,比如questions[0].type,这样Superforms才能正确将DOM值与$form.questions中的对应元素关联。
  • 使用bind:value确保输入值与$form状态双向同步,这样你修改输入时$form会自动更新,反之更新$form时输入也会同步。
  • 即使是初始为空的字段(比如question、answers),也需要渲染对应的DOM元素,否则提交时这些字段不会被包含在表单数据中。

验证后端

后端代码目前是正确的,只要前端提交的表单数据包含questions数组,superValidate会自动解析并验证。你可以在后端打印request.formData()来确认是否收到数据:

export const actions = {
    default: async (request) => {
        const formData = await request.formData();
        console.log('表单数据:', Object.fromEntries(formData)); // 检查是否有questions相关字段
        const form = await superValidate(request, zod(examSchema));
        console.log('验证后的数据:', form);
        return { form };
    }
} satisfies Actions;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 19:07:22