svelte-forms-lib icon indicating copy to clipboard operation
svelte-forms-lib copied to clipboard

Support to set field/s error/s imperatively (`setFieldError`/`setErrors`)

Open EstebanBorai opened this issue 2 years ago • 6 comments

Set errors imperatively on certain field/s, while validating (inside the callback provided to onSubmit) a form or after creating a form.

Problem to solve

As a user I want to be able to call a function which sets an error message on certain field/s.

Intended users

Developer

User experience goal

After creating a form, setting a field's error

const { setFieldError } = createForm({ /* ... */ });

/*  Checks if the provided username is available */
async function validateUsernameServerSide(username) {
  try {
     // -- snip --
  } catch (error) {
    setFieldError('username', `The username: "${username}" is already taken!`,
  }
}

Inside the onSubmit callback

If you wanted to set a field error message from a server side validation, you could use helpers from the onSubmit function which would let you set the message for a certain field.

const form = createForm({
  onSubmit: (values, helpers) => {
    try {
     // -- snip --
    } catch (error) {
      helpers.setFieldError('username', `The username: "${username}" is already taken!`,
    }
  }
});

Proposal

Provide setFieldError for single fields and setErrors functions to the API.

  • setFieldError(field: string, message: string): Sets the provided message as the error for the field with the name provided on field.

Eg:

setFieldError('email', 'The email must belong to the domain "example.com"');
  • setErrors(errors: { [field: string]: string }): A function that receives an object which holds the field name as the key and the error message as the value. Useful to set many error messages at once.
setErrors({
    email: 'The email must belong to the domain "example.com"',
    password: 'The password is not strong enough',
});

Further details

Setting errors outside of the validation schema when form is validated server-side or by functions that run in parallel and can't be validated by the validation schema.

Documentation

Links / references

  • Formik's setFieldError: https://formik.org/docs/api/formik#setfielderror-field-string-errormsg-string--void
  • Formiks's setErrors:

EstebanBorai avatar Feb 15 '22 20:02 EstebanBorai

This would be great! But today what is the current best approach to handling a server-side rejection like this?

ecker00 avatar Jun 25 '22 20:06 ecker00

This would be great! But today what is the current best approach to handling a server-side rejection like this?

I ended up writing straight to the error store.

EstebanBorai avatar Jun 25 '22 22:06 EstebanBorai

I'm assuming it's something like this:

const { errors } = createForm({
  initialValues: {
    name: 'test'
  },
  onSubmit: () => {}
});

errors.set({ name: 'My custom error' });

But I'm using the <Form> component approach (not createForm), so seems I don't have access to the error store directly. Any idea on how to get access to that from <Form>?

ecker00 avatar Jun 26 '22 04:06 ecker00

I'm assuming it's something like this:

const { errors } = createForm({
  initialValues: {
    name: 'test'
  },
  onSubmit: () => {}
});

errors.set({ name: 'My custom error' });

But I'm using the <Form> component approach (not createForm), so seems I don't have access to the error store directly. Any idea on how to get access to that from <Form>?

Oh! Not sure how it would work for the <Form> component approach TBH.

EstebanBorai avatar Jun 26 '22 05:06 EstebanBorai

I can see the <Form> component exposes the underlying errors store as prop, by passing it to the <slot />. Here is the code: https://github.com/tjinauyeung/svelte-forms-lib/blob/7df1b1df4f02c5ff512e2542c879056ac2d8138d/lib/components/Form.svelte#L51

You should be able to use it like:

<Form let:errors={errors}>
  <!-- Do something with the error store here -->
</Form >

Perhaps this way you can write to this?

EstebanBorai avatar Jun 26 '22 05:06 EstebanBorai

Thanks for the pointers. But could not get it to work with the errors slot, when trying to call it from the onSubmit() event of the parent (Function called outside component initialization). But it worked when binding the entire Form and digging out the error store from that, bit hacky, but works.

  onSubmit: (values) => {
    // Send request and get response
    
    const errors = form.$$.ctx.find((a) => a && a.errors).errors; // Find the error store
    errors.set({ name: 'Manual error set' });
  }
};

let form;
<Form {...formProps} bind:this={form}>

ecker00 avatar Jun 27 '22 10:06 ecker00

Thanks @ecker00!

EstebanBorai avatar Jan 26 '23 04:01 EstebanBorai