SvelteKit中提交表单组件时如何阻止页面刷新?
问题分析与解决方案
核心问题
- 全局状态冲突:你用全局的
linkType和linkResult变量存储下拉框值,但表格每行是独立条目,共享变量会导致所有行的下拉框值同步,提交时无法正确对应每行的选择。 - 下拉框未绑定状态:表格中的下拉框没有绑定到状态变量,也未触发更新事件,导致选择的值无法同步到变量中。
- 不必要的默认行为阻止:更新表单中同时使用
use:enhance和event.preventDefault(),二者可能冲突,use:enhance本身已阻止页面默认刷新行为。
修复步骤
1. 为每行数据创建独立状态
在#each循环中,为每行声明局部的rowLinkType和rowLinkResult变量,替换全局共享变量:
{#each form.response as row} <!-- 为每行创建局部状态 --> {#set rowLinkType = row.linkType || ''} {#set rowLinkResult = row.linkResult || ''} <div class="overflow-x-auto"> <!-- 表格内容 --> </div> {/each}
2. 绑定下拉框到局部状态
修改表格中的下拉框,添加bind:value绑定到局部变量,无需额外的on:change事件:
<!-- Link Type下拉框 --> <select bind:value={rowLinkType} class="block rounded-md border border-gray-300 bg-white px-3 py-2 shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-indigo-500 sm:text-sm" > {#each linkTypes as type} <option value={type}>{type}</option> {/each} </select> <!-- Link Result下拉框 --> <select bind:value={rowLinkResult} class="block rounded-md border border-gray-300 bg-white px-3 py-2 shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-indigo-500 sm:text-sm" > {#each linkResults as result} <option value={result}>{result}</option> {/each} </select>
3. 修复更新表单
移除on:submit={(event) => event.preventDefault()},并将隐藏输入的值改为局部变量:
<form action="?/update" method="POST" use:enhance > <input type="hidden" value={row.id} name="id" /> <input type="hidden" value={rowLinkType} name="linkType" /> <input type="hidden" value={rowLinkResult} name="linkResult" /> <button type="submit" class="rounded bg-blue-500 px-4 py-2 text-white hover:bg-blue-700" >Update</button> </form>
4. 移除全局冗余代码
删除全局的linkType、linkResult变量以及对应的handleLinkTypeChange、handleLinkResultChange函数,因为现在每行使用独立状态。
修改后的完整Svelte组件代码
<script> import { enhance } from '$app/forms'; export let data; let selectedUser = ''; let selectedCourse = ''; function handleUserChange(event) { selectedUser = event.target.value; selectedCourse = ''; } function handleCourseChange(event) { selectedCourse = event.target.value; } async function showSelection() { console.log('Selected User:', selectedUser); console.log('Selected Course:', selectedCourse); } export let form; let linkTypes = ['Type1', 'Type']; let linkResults = ['Result1', 'Result2']; </script> <div> <h1 class="text-white-700 text-center text-4xl font-bold">Select Client Data</h1> {#if data} <form action="?/userSelection" method="POST" use:enhance class="flex flex-col items-center space-y-4 p-10" > <select name="user" class="select select-accent w-full max-w-xs" value={selectedUser} on:change={handleUserChange} > <option disabled selected>Select a User</option> {#each data.userData as user} <option value={user.user}>{user.user}</option> {/each} </select> {#if selectedUser !== ''} <select name="course" class="select select-accent w-full max-w-xs" value={selectedCourse} on:change={handleCourseChange} > <option disabled selected>Select a Course</option> {#each data.userData.find((user) => user.user === selectedUser)?.courses || [] as course} <option value={course}>{course}</option> {/each} </select> {/if} <button type="submit" on:click={showSelection} class="btn btn-primary">Get Data</button> </form> {:else} <p>Loading...</p> {/if} {#if form && form.response} {#each form.response as row} {#set rowLinkType = row.linkType || ''} {#set rowLinkResult = row.linkResult || ''} <div class="overflow-x-auto"> <table class="min-w-full divide-y divide-gray-200"> <thead class="bg-gray-50"> <tr> <th scope="col" class="w-1/5 px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500" >ID</th> <th scope="col" class="w-1/5 px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500" >Link</th> <th scope="col" class="w-2/5 px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500" >Link Type</th> <th scope="col" class="w-2/5 px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500" >Link Result</th> <th scope="col" class="w-2/5 px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500" >Update</th> </tr> </thead> <tbody class="divide-y divide-gray-200 bg-white"> <tr class="border-b border-gray-200 hover:bg-gray-100"> <td class="whitespace-nowrap px-6 py-4">{row.id}</td> <td class="whitespace-nowrap px-6 py-4" style="max-width: 300px; overflow-x: auto; width: 100%;" ><a href={row.url} class="truncate">{row.url}</a></td> <td class="whitespace-nowrap px-6 py-4"> <select bind:value={rowLinkType} class="block rounded-md border border-gray-300 bg-white px-3 py-2 shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-indigo-500 sm:text-sm" > {#each linkTypes as type} <option value={type}>{type}</option> {/each} </select> </td> <td class="whitespace-nowrap px-6 py-4"> <select bind:value={rowLinkResult} class="block rounded-md border border-gray-300 bg-white px-3 py-2 shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-indigo-500 sm:text-sm" > {#each linkResults as result} <option value={result}>{result}</option> {/each} </select> </td> <td class="whitespace-nowrap px-6 py-4 text-right"> <form action="?/update" method="POST" use:enhance > <input type="hidden" value={row.id} name="id" /> <input type="hidden" value={rowLinkType} name="linkType" /> <input type="hidden" value={rowLinkResult} name="linkResult" /> <button type="submit" class="rounded bg-blue-500 px-4 py-2 text-white hover:bg-blue-700" >Update</button> </form> </td> </tr> </tbody> </table> </div> {/each} {:else} <div><p>Select User First</p></div> {/if} </div>
额外说明
#set是Svelte 4+的语法,用于循环内声明局部变量;若使用旧版本,可改用嵌套组件或{@const}实现每行独立状态。- 现在每行下拉框值独立存储,
use:enhance会正常处理无刷新提交,页面不会再刷新。 - 若要实际更新PocketBase数据,可修改后端
update函数:
update: async ({ request, locals }) => { const formData = await request.formData(); const id = formData.get("id"); const linkType = formData.get("linkType"); const linkResult = formData.get("linkResult"); try { await locals.pb.collection("Orbittest").update(id, { linkType, linkResult }); return { success: true, id }; } catch (error) { console.error("Update error:", error); return { error: true, message: error.message }; } }
内容的提问来源于stack exchange,提问作者What
相关产品推荐
相关产品推荐

