Skip to content
Open
5 changes: 5 additions & 0 deletions .changeset/chatty-bugs-pump.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': patch
---

fix fields.value() returning undefined when input_value is passed to .as()
6 changes: 6 additions & 0 deletions packages/kit/src/runtime/form-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { DEV } from 'esm-env';
import * as devalue from 'devalue';
import { text_decoder, text_encoder } from './utils.js';
import { SvelteKitError } from '@sveltejs/kit/internal';
import { untrack } from 'svelte';

/**
* Sets a value in a nested object using a path string, mutating the original object
Expand Down Expand Up @@ -679,6 +680,11 @@ export function create_field_proxy(target, get_input, set_input, get_issues, pat
? 'b:'
: '';

// If the input has a default value and there is no current value, set it to the default value
if (input_value !== undefined && get_value() == null) {
untrack(() => set_input(path, input_value));
}

// Base properties for all input types
/** @type {Record<string, any>} */
const base_props = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,28 @@
{/each}

{#each values as value (value.id)}
{@const form = as_value_form.for(value.id)}
<form
{...as_value_form.for(value.id).enhance(({ submit }) => {
{...form.enhance(({ submit }) => {
// TODO: needed to keep the values when JS is enabled, remove when reset is implemented
submit();
})}
>
<input {...as_value_form.fields.text_field.as('text', value.text_field)} />
<input {...form.fields.text_field.as('text', value.text_field)} />

<input {...as_value_form.fields.number_field.as('number', value.number_field)} />
<input {...form.fields.number_field.as('number', value.number_field)} />

<select {...as_value_form.fields.select_field.as('select', value.select_field)}>
<select {...form.fields.select_field.as('select', value.select_field)}>
<option>apple</option>
<option>banana</option>
<option>cherry</option>
</select>

<input {...as_value_form.fields.color_field.as('color', value.color_field)} />
<input {...form.fields.color_field.as('color', value.color_field)} />

<input {...as_value_form.fields.range_field.as('range', value.range_field)} />
<input {...form.fields.range_field.as('range', value.range_field)} />

<button {...as_value_form.fields.id.as('submit', value.id)}>submit</button>
<button {...form.fields.id.as('submit', value.id)}>submit</button>
</form>
{/each}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<script>
import { test_form } from './form.remote';
</script>

<form {...test_form}>
<p id="default-value-display">{test_form.fields.name.value()}</p>
<input
data-testid="as-value-default-input"
{...test_form.fields.name.as('text', 'default text')}
/>
</form>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { form } from '$app/server';
import * as v from 'valibot';

export const test_form = form(v.object({ name: v.string() }), async (data) => {});
13 changes: 13 additions & 0 deletions packages/kit/test/apps/async/test/client.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,19 @@ test.describe('remote function mutations', () => {
}
});

test('.value() reflects input_value default before any user interaction', async ({ page }) => {
await page.goto('/remote/form/value-default');

const input = page.locator('[data-testid="as-value-default-input"]');
const display = page.locator('#default-value-display');

// input has to output the default value provided
await expect(input).toHaveValue('default text');

// value() also needs to output the default value provided WITHOUT any user interaction
await expect(display).toHaveText('default text');
});

test('.as(type, value) updates when data changes after submission', async ({ page }) => {
await page.goto('/remote/form/as-value');

Expand Down
Loading