ciscoheat / sveltekit-superforms

Making SvelteKit forms a pleasure to use!
https://superforms.rocks
MIT License
2.15k stars 65 forks source link

Form with a field that is a refined array of fields will not clear errors when fields within more than one array are tainted #196

Closed phobetron closed 1 year ago

phobetron commented 1 year ago

Description

We have a form with a field that holds an array of fields. To keep it simple, the form can have one array field, and each object in the array corresponds to one enum-based select field each. We want to validate that at least one of the fields has a non-empty value, so we use refine on the array.

The schemas look something like this:

const childSchema = z.object({ option: z.enum(['', 'one', 'two', 'three']) });
const parentSchema = z.object({
  children: childSchema.array().refine((children) => children.some((child) => child.option.length > 0)),
});

We will also create an initial set of values, so exactly two entries will be in the array:

const initialValues = {
  children: [{ option: '' }, { option: '' }],
};

We'll then initialize the form (as an SPA, in case it matters):

const { enhance, form, allErrors } = superForm(superValidateSync(initialValues, parentSchema), {
  SPA: true,
  dataType: 'json', // set according to the docs
  validators: parentSchema,
});

Then we will add our simple form, with a button that is disabled when the form is invalid (for ease of testing):

<form method="POST" use:enhance>
  {#each $form.children as _child, i}
    <p>
      <label for={`options-${i}`}>Child {i + 1}</label>
      <select id={`options-${i}`} bind:value={$form.children[i].option}>
        <option value="">None</option>
        <option value="one">One</option>
        <option value="two">Two</option>
        <option value="three">Three</option>
      </select>
    </p>
  {/each}

  <button type="submit" disabled={$allErrors.length > 0}> Submit </button>
</form>

When we change one of the select fields back-and-forth, we can see the button enabling and disabling as expected. However, if we then begin to change the second select field's value and enter an error state, the error persists even when the form should be valid.

If we add $: console.log($form, $errors); inside the script, we will see that, once the form reaches an error state with two tainted fields, the errors store no longer updates. However, the form store does reflect the current values.

For a short-term work-around, we can remove the refine function from the schema and add a bit of logic in onUpdate, along with some other trickery, but that isn't really optimal.

We have not had this problem with similar schemas in Felte, but we're trying to migrate away from Felte to Superforms, so we can enjoy some of its awesome features.

MRE

https://stackblitz.com/edit/sveltekit-superforms-bug-reporting?file=src%2Froutes%2F%2Bpage.server.ts,src%2Froutes%2F%2Bpage.svelte](https://stackblitz.com/edit/sveltekit-superforms-bug-reporting-fnuh1c?file=src%2Froutes%2F%2Bpage.svelte)

ciscoheat commented 1 year ago

Thank you for reporting and the MRE, will look into this for the next patch release, will be quite soon!

ciscoheat commented 1 year ago

Will be fixed in 1.0.1, released today or tomorrow.