From 8fd0b76f7c1ee3aa8ce4c5c812e455346b42342f Mon Sep 17 00:00:00 2001 From: LeCarbonator <18158911+LeCarbonator@users.noreply.github.com> Date: Thu, 17 Apr 2025 23:41:33 +0200 Subject: [PATCH 1/8] docs: expand submission handling in react guides --- .../react/guides/submission-handling.md | 56 ++++++++++++++----- docs/framework/react/guides/validation.md | 15 ++++- 2 files changed, 55 insertions(+), 16 deletions(-) diff --git a/docs/framework/react/guides/submission-handling.md b/docs/framework/react/guides/submission-handling.md index bc99f2a17..de7601571 100644 --- a/docs/framework/react/guides/submission-handling.md +++ b/docs/framework/react/guides/submission-handling.md @@ -3,38 +3,68 @@ id: submission-handling title: Submission handling --- -In a situation where you want to have multiple form submission types, for example, a form that has a button that navigates to a sub-form and another button that handles a standard submission, you can make use of the onSubmitMeta prop and the handleSubmit function overloads. +## Passing additional data to submission handling -## Basic Usage +You may want to pass additional information to your submission handler, for example, which section to navigate to. +To pass meta data to `onSubmit`, you can make use of the `onSubmitMeta` property. -First you must define the default state of the form.onSubmitMeta prop: +> Note: if `form.handleSubmit()` is called without metadata, it will use the provided default. ```tsx const form = useForm({ defaultValues: { firstName: 'Rick', }, - // {} is the default value passed to `onSubmit`'s `meta` property - onSubmitMeta: {} as { lastName: string }, + // Specify what values to use as default if no meta is passed + onSubmitMeta: { lastName: 'The default value if no submit Meta is passed' }, onSubmit: async ({ value, meta }) => { // Do something with the values passed via handleSubmit - console.log(`${value.firstName} - ${meta}`) + console.log(`${value.firstName} - ${meta.lastName}`) }, }) -``` - -Note: the default state of onSubmitMeta is `never`, so if the prop is not provided and you try to access it in `handleSubmit`, or `onSubmit` it will error. - -Then when you call `onSubmit` you can provide it the predefined meta like so: -```tsx
{ e.preventDefault() e.stopPropagation() + // Overwrites the default specified in onSubmitMeta form.handleSubmit({ lastName: 'Astley', }) + + // form.handleSubmit() would use the default meta }} ->
+> + {/* ... */} + +``` + +## Transforming data with Standard Schemas + +While Tanstack Form provides Standard Schema support for validation, it does not preserve the Schema's output data. + +The value passed to the `onSubmit` function will always be the input data. To receive the output data of a Standard Schema, parse it in the `onSubmit` function: + +```tsx +const schema = z.object({ + age: z.string().transform((age) => Number(age)), +}) + +// Tanstack form uses the input type of Standard Schemas +const defaultValues: z.input = { + age: '13', +} + +const form = useForm({ + defaultValues, + validators: { + onChange: schema, + }, + onSubmit: ({ value }) => { + const inputAge: string = value.age + // Pass it through the schema to get the transformed value + const result = schema.parse(value) + const outputAge: number = result.age + }, +}) ``` diff --git a/docs/framework/react/guides/validation.md b/docs/framework/react/guides/validation.md index 777f03306..0ddb8a5c4 100644 --- a/docs/framework/react/guides/validation.md +++ b/docs/framework/react/guides/validation.md @@ -227,16 +227,23 @@ export default function App() { const form = useForm({ defaultValues: { age: 0, + socials: [], + details: { + email: '', + }, }, validators: { onSubmitAsync: async ({ value }) => { - // Verify the age on the server - const isOlderThan13 = await verifyAgeOnServer(value.age) - if (!isOlderThan13) { + // Validate the value on the server + const hasErrors = await verifyDataOnServer(value) + if (hasErrors) { return { form: 'Invalid data', // The `form` key is optional fields: { age: 'Must be 13 or older to sign', + // Set errors on nested fields with the field's name + 'socials[0].url': 'The provided URL does not exist' + 'details.email': 'An email is required', }, } } @@ -450,6 +457,8 @@ TanStack Form natively supports all libraries following the [Standard Schema spe _Note:_ make sure to use the latest version of the schema libraries as older versions might not support Standard Schema yet. +> Validation will not provide you with transformed values. See [submission handling](./submission-handling.md) for more information. + To use schemas from these libraries you can pass them to the `validators` props as you would do with a custom function: ```tsx From 29810425e1e4806e422e68cb88eb1e763f9e0eaf Mon Sep 17 00:00:00 2001 From: LeCarbonator <18158911+LeCarbonator@users.noreply.github.com> Date: Fri, 18 Apr 2025 08:03:47 +0200 Subject: [PATCH 2/8] docs: update vue validation/submission handling pages --- docs/framework/react/guides/validation.md | 2 +- .../vue/guides/submission-handling.md | 82 +++++++++++++++++ docs/framework/vue/guides/validation.md | 87 +++++++++++++++++++ 3 files changed, 170 insertions(+), 1 deletion(-) create mode 100644 docs/framework/vue/guides/submission-handling.md diff --git a/docs/framework/react/guides/validation.md b/docs/framework/react/guides/validation.md index 0ddb8a5c4..be0ff3fc9 100644 --- a/docs/framework/react/guides/validation.md +++ b/docs/framework/react/guides/validation.md @@ -440,7 +440,7 @@ This will debounce every async call with a 500ms delay. You can even override th /> ``` -> This will run `onChangeAsync` every 1500ms while `onBlurAsync` will run every 500ms. +This will run `onChangeAsync` every 1500ms while `onBlurAsync` will run every 500ms. ## Validation through Schema Libraries diff --git a/docs/framework/vue/guides/submission-handling.md b/docs/framework/vue/guides/submission-handling.md new file mode 100644 index 000000000..fec403eca --- /dev/null +++ b/docs/framework/vue/guides/submission-handling.md @@ -0,0 +1,82 @@ +--- +id: submission-handling +title: Submission handling +--- + +## Passing additional data to submission handling + +You may want to pass additional information to your submission handler, for example, which section to navigate to. +To pass meta data to `onSubmit`, you can make use of the `onSubmitMeta` property. + +> Note: if `form.handleSubmit()` is called without metadata, it will use the provided default. + +```vue + + + +``` + +## Transforming data with Standard Schemas + +While Tanstack Form provides Standard Schema support for validation, it does not preserve the Schema's output data. + +The value passed to the `onSubmit` function will always be the input data. To receive the output data of a Standard Schema, parse it in the `onSubmit` function: + +```vue + + +``` diff --git a/docs/framework/vue/guides/validation.md b/docs/framework/vue/guides/validation.md index 6a9db48fb..50b19674b 100644 --- a/docs/framework/vue/guides/validation.md +++ b/docs/framework/vue/guides/validation.md @@ -225,6 +225,91 @@ const formErrorMap = form.useStore((state) => state.errorMap) ``` +### Setting field-level errors from the form's validators + +You can set errors on the fields from the form's validators. One common use case for this is validating all the fields on submit by calling a single API endpoint in the form's `onSubmitAsync` validator. + +```vue + + + +``` + +> Something worth mentioning is that if you have a form validation function that returns an error, that error may be overwritten by the field-specific validation. +> +> This means that: +> +> ```vue +> +> +> +> ``` +> +> Will only show `'Must be odd!` even if the 'Too young!' error is returned by the form-level validation. + ## Asynchronous Functional Validation While we suspect most validations will be synchronous, there are many instances where a network call or some other async operation would be useful to validate against. @@ -385,6 +470,8 @@ TanStack Form natively supports all libraries following the [Standard Schema spe _Note:_ make sure to use the latest version of the schema libraries as older versions might not support Standard Schema yet. +> Validation will not provide you with transformed values. See [submission handling](./submission-handling.md) for more information. + To use schemas from these libraries you can pass them to the `validators` props as you would do with a custom function: ```vue From beb25a6a4b7a024589f26944faebc7a93944a143 Mon Sep 17 00:00:00 2001 From: LeCarbonator <18158911+LeCarbonator@users.noreply.github.com> Date: Fri, 18 Apr 2025 10:59:27 +0200 Subject: [PATCH 3/8] docs: update solid validation/submission handling page --- .../react/guides/submission-handling.md | 92 +++++++++---- docs/framework/react/guides/validation.md | 2 +- .../solid/guides/submission-handling.md | 111 ++++++++++++++++ docs/framework/solid/guides/validation.md | 124 +++++++++++++++++- .../vue/guides/submission-handling.md | 50 +++++-- docs/framework/vue/guides/validation.md | 24 ++-- 6 files changed, 347 insertions(+), 56 deletions(-) create mode 100644 docs/framework/solid/guides/submission-handling.md diff --git a/docs/framework/react/guides/submission-handling.md b/docs/framework/react/guides/submission-handling.md index de7601571..50e70c995 100644 --- a/docs/framework/react/guides/submission-handling.md +++ b/docs/framework/react/guides/submission-handling.md @@ -5,43 +5,79 @@ title: Submission handling ## Passing additional data to submission handling -You may want to pass additional information to your submission handler, for example, which section to navigate to. -To pass meta data to `onSubmit`, you can make use of the `onSubmitMeta` property. +You may want to split your long form into multiple sections, for example, when creating a booking form. +To receive meta data in your `onSubmit` function, you can specify the `onSubmitMeta` property. > Note: if `form.handleSubmit()` is called without metadata, it will use the provided default. ```tsx -const form = useForm({ - defaultValues: { - firstName: 'Rick', - }, - // Specify what values to use as default if no meta is passed - onSubmitMeta: { lastName: 'The default value if no submit Meta is passed' }, - onSubmit: async ({ value, meta }) => { - // Do something with the values passed via handleSubmit - console.log(`${value.firstName} - ${meta.lastName}`) - }, -}) +import { useForm } from '@tanstack/react-form' + +type FormMeta = { + section: 'personalInfo' | 'paymentInfo' | null +} + +// Metadata is not required to call form.handleSubmit(). +// Specify what values to use as default if no meta is passed +const defaultMeta: FormMeta = { + section: null, +} -
{ - e.preventDefault() - e.stopPropagation() - // Overwrites the default specified in onSubmitMeta - form.handleSubmit({ - lastName: 'Astley', - }) +function App() { + const form = useForm({ + defaultValues: { + personal: { + name: '', + email: '', + }, + payment: { + address: '', + }, + }, + // Define what meta values to expect on submission + onSubmitMeta: defaultMeta, + onSubmit: async ({ value, meta }) => { + // Do something with the values passed via handleSubmit + console.log(`Selected section - ${meta.section}`, value) + }, + }) - // form.handleSubmit() would use the default meta - }} -> - {/* ... */} -
+ return ( + <> +
{ + e.preventDefault() + e.stopPropagation() + // Overwrites the default specified in onSubmitMeta + form.handleSubmit({ + section: 'personalInfo', + }) + }} + > + {/* ... */} + +
+ +
{ + e.preventDefault() + e.stopPropagation() + form.handleSubmit({ + section: 'paymentInfo', + }) + }} + > + {/* ... */} + +
+ + ) +} ``` ## Transforming data with Standard Schemas -While Tanstack Form provides Standard Schema support for validation, it does not preserve the Schema's output data. +While Tanstack Form provides [Standard Schema support](./validation.md) for validation, it does not preserve the Schema's output data. The value passed to the `onSubmit` function will always be the input data. To receive the output data of a Standard Schema, parse it in the `onSubmit` function: @@ -50,7 +86,7 @@ const schema = z.object({ age: z.string().transform((age) => Number(age)), }) -// Tanstack form uses the input type of Standard Schemas +// Tanstack Form uses the input type of Standard Schemas const defaultValues: z.input = { age: '13', } diff --git a/docs/framework/react/guides/validation.md b/docs/framework/react/guides/validation.md index be0ff3fc9..7a0e2d036 100644 --- a/docs/framework/react/guides/validation.md +++ b/docs/framework/react/guides/validation.md @@ -242,7 +242,7 @@ export default function App() { fields: { age: 'Must be 13 or older to sign', // Set errors on nested fields with the field's name - 'socials[0].url': 'The provided URL does not exist' + 'socials[0].url': 'The provided URL does not exist', 'details.email': 'An email is required', }, } diff --git a/docs/framework/solid/guides/submission-handling.md b/docs/framework/solid/guides/submission-handling.md new file mode 100644 index 000000000..72dc7812d --- /dev/null +++ b/docs/framework/solid/guides/submission-handling.md @@ -0,0 +1,111 @@ +--- +id: submission-handling +title: Submission handling +--- + +## Passing additional data to submission handling + +You may want to split your long form into multiple sections, for example, when creating a booking form. +To receive meta data in your `onSubmit` function, you can specify the `onSubmitMeta` property. + +> Note: if `form.handleSubmit()` is called without metadata, it will use the provided default. + +```tsx +import { createForm } from '@tanstack/solid-form' + +type FormMeta = { + section: 'personalInfo' | 'paymentInfo' | null +} + +// Metadata is not required to call form.handleSubmit(). +// Specify what values to use as default if no meta is passed +const defaultMeta: FormMeta = { + section: null, +} + +export default function App() { + const form = createForm(() => ({ + defaultValues: { + personal: { + name: '', + email: '', + }, + payment: { + address: '', + }, + }, + // Define what meta values to expect on submission + onSubmitMeta: defaultMeta, + onSubmit: async ({ value, meta }) => { + // Do something with the values passed via handleSubmit + console.log(`Selected section - ${meta.section}`, value) + }, + })) + + return ( + <> +
{ + e.preventDefault() + e.stopPropagation() + // Overwrites the default specified in onSubmitMeta + form.handleSubmit({ + section: 'personalInfo', + }) + }} + > + {/* ... */} + +
+ +
{ + e.preventDefault() + e.stopPropagation() + form.handleSubmit({ + section: 'paymentInfo', + }) + }} + > + {/* ... */} + +
+ + ) +} +``` + +## Transforming data with Standard Schemas + +While Tanstack Form provides [Standard Schema support](./validation.md) for validation, it does not preserve the Schema's output data. + +The value passed to the `onSubmit` function will always be the input data. To receive the output data of a Standard Schema, parse it in the `onSubmit` function: + +```tsx +import { createForm } from '@tanstack/solid-form' +import { z } from 'zod' + +// ... + +const schema = z.object({ + age: z.string().transform((age) => Number(age)), +}) + +// Tanstack Form uses the input type of Standard Schemas +const defaultValues: z.input = { + age: '13', +} + +const form = createForm(() => ({ + defaultValues, + validators: { + onChange: schema, + }, + onSubmit: ({ value }) => { + const inputAge: string = value.age + // Pass it through the schema to get the transformed value + const result = schema.parse(value) + const outputAge: number = result.age + }, +})) +``` diff --git a/docs/framework/solid/guides/validation.md b/docs/framework/solid/guides/validation.md index 6d076e44c..a8588638e 100644 --- a/docs/framework/solid/guides/validation.md +++ b/docs/framework/solid/guides/validation.md @@ -218,6 +218,126 @@ export default function App() { } ``` +### Setting field-level errors from the form's validators + +You can set errors on the fields from the form's validators. One common use case for this is validating all the fields on submit by calling a single API endpoint in the form's `onSubmitAsync` validator. + +```tsx +import { Show } from 'solid-js' +import { createForm } from '@tanstack/solid-form' + +export default function App() { + const form = createForm(() => ({ + defaultValues: { + age: 0, + socials: [], + details: { + email: '', + }, + }, + validators: { + onSubmitAsync: async ({ value }) => { + // Validate the value on the server + const hasErrors = await validateDataOnServer(value) + if (hasErrors) { + return { + form: 'Invalid data', // The `form` key is optional + fields: { + age: 'Must be 13 or older to sign', + // Set errors on nested fields with the field's name + 'socials[0].url': 'The provided URL does not exist', + 'details.email': 'An email is required', + }, + } + } + + return null + }, + }, + })) + + return ( +
+
{ + e.preventDefault() + e.stopPropagation() + void form.handleSubmit() + }} + > + ( + <> + + field().handleChange(e.target.valueAsNumber)} + /> + 0}> + {field().state.meta.errors.join(', ')} + + + )} + /> + ({ errors: state.errors })} + children={(state) => ( + 0}> +
+ + There was an error on the form: {state().errors.join(', ')} + +
+
+ )} + /> + + + {/*...*/} + +
+ ) +} +``` + +> Something worth mentioning is that if you have a form validation function that returns an error, that error may be overwritten by the field-specific validation. +> +> This means that: +> +> ```tsx +> const form = createForm(() => ({ +> defaultValues: { +> age: 0, +> }, +> validators: { +> onChange: ({ value }) => { +> return { +> fields: { +> age: value.age < 12 ? 'Too young!' : undefined, +> }, +> }; +> }, +> }, +> })); +> +> return ( +> name="age" +> validators={{ +> onChange: ({ value }) => (value % 2 === 0 ? 'Must be odd!' : undefined), +> }} +> children={() => <>{/* ... */}} +> /> +> ); +> } +> ``` +> +> Will only show `'Must be odd!` even if the 'Too young!' error is returned by the form-level validation. + ## Asynchronous Functional Validation While we suspect most validations will be synchronous, there are many instances where a network call or some other async operation would be useful to validate against. @@ -328,7 +448,7 @@ This will debounce every async call with a 500ms delay. You can even override th /> ``` -> This will run `onChangeAsync` every 1500ms while `onBlurAsync` will run every 500ms. +This will run `onChangeAsync` every 1500ms while `onBlurAsync` will run every 500ms. ## Validation through Schema Libraries @@ -344,6 +464,8 @@ TanStack Form natively supports all libraries following the [Standard Schema spe _Note:_ make sure to use the latest version of the schema libraries as older versions might not support Standard Schema yet. +> Validation will not provide you with transformed values. See [submission handling](./submission-handling.md) for more information. + To use schemas from these libraries you can pass them to the `validators` props as you would do with a custom function: ```tsx diff --git a/docs/framework/vue/guides/submission-handling.md b/docs/framework/vue/guides/submission-handling.md index fec403eca..e35b735e1 100644 --- a/docs/framework/vue/guides/submission-handling.md +++ b/docs/framework/vue/guides/submission-handling.md @@ -5,22 +5,40 @@ title: Submission handling ## Passing additional data to submission handling -You may want to pass additional information to your submission handler, for example, which section to navigate to. -To pass meta data to `onSubmit`, you can make use of the `onSubmitMeta` property. +You may want to split your long form into multiple sections, for example, when creating a booking form. +To receive meta data in your `onSubmit` function, you can specify the `onSubmitMeta` property. > Note: if `form.handleSubmit()` is called without metadata, it will use the provided default. ```vue @@ -28,24 +46,28 @@ const form = useForm({ ``` ## Transforming data with Standard Schemas -While Tanstack Form provides Standard Schema support for validation, it does not preserve the Schema's output data. +While Tanstack Form provides [Standard Schema support](./validation.md) for validation, it does not preserve the Schema's output data. The value passed to the `onSubmit` function will always be the input data. To receive the output data of a Standard Schema, parse it in the `onSubmit` function: diff --git a/docs/framework/vue/guides/validation.md b/docs/framework/vue/guides/validation.md index 50b19674b..baeab4e47 100644 --- a/docs/framework/vue/guides/validation.md +++ b/docs/framework/vue/guides/validation.md @@ -245,20 +245,20 @@ const form = useForm({ // Add validators to the form the same way you would add them to a field onSubmitAsync({ value }) { // Validate the value on the server - const hasErrors = await verifyDataOnServer(value) - if (hasErrors) { - return { - form: 'Invalid data', // The `form` key is optional - fields: { - age: 'Must be 13 or older to sign', - // Set errors on nested fields with the field's name - 'socials[0].url': 'The provided URL does not exist' - 'details.email': 'An email is required', - }, - } + const hasErrors = await verifyDataOnServer(value) + if (hasErrors) { + return { + form: 'Invalid data', // The `form` key is optional + fields: { + age: 'Must be 13 or older to sign', + // Set errors on nested fields with the field's name + 'socials[0].url': 'The provided URL does not exist', + 'details.email': 'An email is required', + }, } + } - return null + return null }, }, }) From f3aa88e95f3fa26d7c75d42ff3e69084b81cf1aa Mon Sep 17 00:00:00 2001 From: LeCarbonator <18158911+LeCarbonator@users.noreply.github.com> Date: Fri, 18 Apr 2025 14:15:22 +0200 Subject: [PATCH 4/8] docs: add angular submission handling --- .../angular/guides/submission-handling.md | 113 +++++++++++++++ docs/framework/angular/guides/validation.md | 130 ++++++++++++++++++ docs/framework/react/guides/validation.md | 1 + 3 files changed, 244 insertions(+) create mode 100644 docs/framework/angular/guides/submission-handling.md diff --git a/docs/framework/angular/guides/submission-handling.md b/docs/framework/angular/guides/submission-handling.md new file mode 100644 index 000000000..b7175537e --- /dev/null +++ b/docs/framework/angular/guides/submission-handling.md @@ -0,0 +1,113 @@ +--- +id: submission-handling +title: Submission handling +--- + +## Passing additional data to submission handling + +You may want to split your long form into multiple sections, for example, when creating a booking form. +To receive meta data in your `onSubmit` function, you can specify the `onSubmitMeta` property. + +> Note: if `form.handleSubmit()` is called without metadata, it will use the provided default. + +```angular-ts +import { Component } from '@angular/core'; +import { injectForm } from '@tanstack/angular-form'; + +type FormMeta = { + section: 'personalInfo' | 'paymentInfo' | null; +}; + +// Metadata is not required to call form.handleSubmit(). +// Specify what values to use as default if no meta is passed +const defaultMeta: FormMeta = { + section: null, +}; + +@Component({ + selector: 'app-root', + standalone: true, + template: ` +
+
+ + +
+
+ + +
+
+ `, +}) + +export class AppComponent { + name = 'Angular'; + form = injectForm({ + defaultValues: { + personal: { + name: '', + email: '', + }, + payment: { + address: '', + }, + }, + // Define what meta values to expect on submission + onSubmitMeta: defaultMeta, + onSubmit: async ({ value, meta }) => { + // Do something with the values passed via handleSubmit + console.log(`Selected section - ${meta.section}`, value); + }, + }); + + handleSubmit(event: SubmitEvent, meta: FormMeta) { + event.preventDefault(); + event.stopPropagation(); + // Overwrites the default specified in onSubmitMeta + this.form.handleSubmit(meta); + } +} +``` + +## Transforming data with Standard Schemas + +While Tanstack Form provides [Standard Schema support](./validation.md) for validation, it does not preserve the Schema's output data. + +The value passed to the `onSubmit` function will always be the input data. To receive the output data of a Standard Schema, parse it in the `onSubmit` function: + +```tsx +import { z } from 'zod' +// ... + +const schema = z.object({ + age: z.string().transform((age) => Number(age)), +}) + +// Tanstack Form uses the input type of Standard Schemas +const defaultValues: z.input = { + age: '13', +} + +// ... + +export class AppComponent { + name = 'Angular' + form = injectForm({ + defaultValues, + validators: { + onChange: schema, + }, + onSubmit: ({ value }) => { + const inputAge: string = value.age + // Pass it through the schema to get the transformed value + const result = schema.parse(value) + const outputAge: number = result.age + }, + }) +} +``` diff --git a/docs/framework/angular/guides/validation.md b/docs/framework/angular/guides/validation.md index 608817624..76213f0cf 100644 --- a/docs/framework/angular/guides/validation.md +++ b/docs/framework/angular/guides/validation.md @@ -288,6 +288,134 @@ export class AppComponent { } ``` +### Setting field-level errors from the form's validators + +You can set errors on the fields from the form's validators. One common use case for this is validating all the fields on submit by calling a single API endpoint in the form's `onSubmitAsync` validator. + +```angular-ts +@Component({ + selector: 'app-root', + standalone: true, + imports: [TanStackField], + template: ` +
+
+ + + + @if (ageField.api.state.meta.errors.length > 0) { + {{ ageField.api.state.meta.errors.join(', ') }} + } + +
+ +
+ `, +}) + +export class AppComponent { + name = 'Angular'; + form = injectForm({ + defaultValues: { + age: 0, + socials: [], + details: { + email: '', + }, + }, + validators: { + onSubmitAsync: async ({ value }) => { + // Validate the value on the server + const hasErrors = await verifyDataOnServer(value) + if (hasErrors) { + return { + form: 'Invalid data', // The `form` key is optional + fields: { + age: 'Must be 13 or older to sign', + // Set errors on nested fields with the field's name + 'socials[0].url': 'The provided URL does not exist', + 'details.email': 'An email is required', + }, + }; + } + + return null; + }, + }, + }); + + handleSubmit(event: SubmitEvent) { + event.preventDefault(); + event.stopPropagation(); + this.form.handleSubmit(); + } +} +``` + +> Something worth mentioning is that if you have a form validation function that returns an error, that error may be overwritten by the field-specific validation. +> +> This means that: +> +> ```angular-ts +> @Component({ +> selector: 'app-root', +> standalone: true, +> imports: [TanStackField], +> template: ` +>
+> [tanstackField]="form" +> name="age" +> #ageField="field" +> [validators]="{ +> onChange: fieldValidator +> }" +> > +> (input)="ageField.api.handleChange($any($event).target.valueAsNumber)" +> /> +> @if (ageField.api.state.meta.errors.length > 0) { +> {{ ageField.api.state.meta.errors.join(', ') }} +> } +> +>
+> `, +> }) +> export class AppComponent { +> name = 'Angular'; +> form = injectForm({ +> defaultValues: { +> age: 0, +> }, +> validators: { +> onChange: ({ value }) => { +> return { +> fields: { +> age: value.age < 12 ? 'Too young!' : undefined, +> }, +> }; +> }, +> }, +> }); +> +> fieldValidator: FieldValidateFn = ({ value }) => +> value % 2 === 0 ? 'Must be odd!' : undefined; +> } +> +> ``` +> +> Will only show `'Must be odd!` even if the 'Too young!' error is returned by the form-level validation. + ## Asynchronous Functional Validation While we suspect most validations will be synchronous, there are many instances where a network call or some other async operation would be useful to validate against. @@ -429,6 +557,8 @@ TanStack Form natively supports all libraries following the [Standard Schema spe _Note:_ make sure to use the latest version of the schema libraries as older versions might not support Standard Schema yet. +> Validation will not provide you with transformed values. See [submission handling](./submission-handling.md) for more information. + To use schemas from these libraries you can pass them to the `validators` props as you would do with a custom function: ```angular-ts diff --git a/docs/framework/react/guides/validation.md b/docs/framework/react/guides/validation.md index 7a0e2d036..e9bdee07c 100644 --- a/docs/framework/react/guides/validation.md +++ b/docs/framework/react/guides/validation.md @@ -324,6 +324,7 @@ export default function App() { > validators={{ > onChange: ({ value }) => (value % 2 === 0 ? 'Must be odd!' : undefined), > }} +> children={() => <>{/* ... */}} > /> > ) > ``` From 5b9a71fd42859ce1e4f8cb35a0a1deeb7bef5864 Mon Sep 17 00:00:00 2001 From: LeCarbonator <18158911+LeCarbonator@users.noreply.github.com> Date: Fri, 18 Apr 2025 14:47:50 +0200 Subject: [PATCH 5/8] Update docs/framework/angular/guides/submission-handling.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Pascal Küsgen --- docs/framework/angular/guides/submission-handling.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/framework/angular/guides/submission-handling.md b/docs/framework/angular/guides/submission-handling.md index b7175537e..0afb5209c 100644 --- a/docs/framework/angular/guides/submission-handling.md +++ b/docs/framework/angular/guides/submission-handling.md @@ -44,7 +44,6 @@ const defaultMeta: FormMeta = { `, }) - export class AppComponent { name = 'Angular'; form = injectForm({ From ef685b1cd6ee97b67f36db1dc9ddeaa4e25358b6 Mon Sep 17 00:00:00 2001 From: LeCarbonator <18158911+LeCarbonator@users.noreply.github.com> Date: Fri, 18 Apr 2025 16:34:51 +0200 Subject: [PATCH 6/8] Update docs/framework/angular/guides/submission-handling.md --- docs/framework/angular/guides/submission-handling.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/framework/angular/guides/submission-handling.md b/docs/framework/angular/guides/submission-handling.md index 0afb5209c..a7205af06 100644 --- a/docs/framework/angular/guides/submission-handling.md +++ b/docs/framework/angular/guides/submission-handling.md @@ -94,6 +94,9 @@ const defaultValues: z.input = { // ... +@Component({ + // ... +}) export class AppComponent { name = 'Angular' form = injectForm({ From 7d12469ee4ef5be47bffdd9094f165020c7a3358 Mon Sep 17 00:00:00 2001 From: LeCarbonator <18158911+LeCarbonator@users.noreply.github.com> Date: Fri, 18 Apr 2025 21:43:32 +0200 Subject: [PATCH 7/8] docs: change example for submit meta --- .../angular/guides/submission-handling.md | 47 ++++++-------- .../react/guides/submission-handling.md | 63 ++++++++----------- .../solid/guides/submission-handling.md | 63 ++++++++----------- .../vue/guides/submission-handling.md | 53 +++++++--------- 4 files changed, 93 insertions(+), 133 deletions(-) diff --git a/docs/framework/angular/guides/submission-handling.md b/docs/framework/angular/guides/submission-handling.md index b7175537e..a9b7db998 100644 --- a/docs/framework/angular/guides/submission-handling.md +++ b/docs/framework/angular/guides/submission-handling.md @@ -5,8 +5,8 @@ title: Submission handling ## Passing additional data to submission handling -You may want to split your long form into multiple sections, for example, when creating a booking form. -To receive meta data in your `onSubmit` function, you can specify the `onSubmitMeta` property. +You may have multiple types of submission behaviour, for example, going back to another page or staying on the form. +You can accomplish this by specifying the `onSubmitMeta` property. This meta data will be passed to the `onSubmit` function. > Note: if `form.handleSubmit()` is called without metadata, it will use the provided default. @@ -14,60 +14,51 @@ To receive meta data in your `onSubmit` function, you can specify the `onSubmitM import { Component } from '@angular/core'; import { injectForm } from '@tanstack/angular-form'; + type FormMeta = { - section: 'personalInfo' | 'paymentInfo' | null; + submitAction: 'continue' | 'backToMenu' | null; }; // Metadata is not required to call form.handleSubmit(). // Specify what values to use as default if no meta is passed const defaultMeta: FormMeta = { - section: null, + submitAction: null, }; @Component({ selector: 'app-root', standalone: true, template: ` -
-
- - -
-
- - -
-
+
+ + +
`, }) - export class AppComponent { name = 'Angular'; form = injectForm({ defaultValues: { - personal: { - name: '', - email: '', - }, - payment: { - address: '', - }, + data: '', }, // Define what meta values to expect on submission onSubmitMeta: defaultMeta, onSubmit: async ({ value, meta }) => { // Do something with the values passed via handleSubmit - console.log(`Selected section - ${meta.section}`, value); + console.log(`Selected action - ${meta.submitAction}`, value); }, }); - handleSubmit(event: SubmitEvent, meta: FormMeta) { + handleSubmit(event: SubmitEvent) { event.preventDefault(); event.stopPropagation(); + } + + handleClick(meta: FormMeta) { // Overwrites the default specified in onSubmitMeta this.form.handleSubmit(meta); } diff --git a/docs/framework/react/guides/submission-handling.md b/docs/framework/react/guides/submission-handling.md index 50e70c995..a7fe65ca7 100644 --- a/docs/framework/react/guides/submission-handling.md +++ b/docs/framework/react/guides/submission-handling.md @@ -5,8 +5,8 @@ title: Submission handling ## Passing additional data to submission handling -You may want to split your long form into multiple sections, for example, when creating a booking form. -To receive meta data in your `onSubmit` function, you can specify the `onSubmitMeta` property. +You may have multiple types of submission behaviour, for example, going back to another page or staying on the form. +You can accomplish this by specifying the `onSubmitMeta` property. This meta data will be passed to the `onSubmit` function. > Note: if `form.handleSubmit()` is called without metadata, it will use the provided default. @@ -14,63 +14,50 @@ To receive meta data in your `onSubmit` function, you can specify the `onSubmitM import { useForm } from '@tanstack/react-form' type FormMeta = { - section: 'personalInfo' | 'paymentInfo' | null + submitAction: 'continue' | 'backToMenu' | null } // Metadata is not required to call form.handleSubmit(). // Specify what values to use as default if no meta is passed const defaultMeta: FormMeta = { - section: null, + submitAction: null, } function App() { const form = useForm({ defaultValues: { - personal: { - name: '', - email: '', - }, - payment: { - address: '', - }, + data: '', }, // Define what meta values to expect on submission onSubmitMeta: defaultMeta, onSubmit: async ({ value, meta }) => { // Do something with the values passed via handleSubmit - console.log(`Selected section - ${meta.section}`, value) + console.log(`Selected action - ${meta.submitAction}`, value) }, }) return ( - <> -
{ - e.preventDefault() - e.stopPropagation() - // Overwrites the default specified in onSubmitMeta - form.handleSubmit({ - section: 'personalInfo', - }) - }} + { + e.preventDefault() + e.stopPropagation() + }} + > + {/* ... */} + -
- -
{ - e.preventDefault() - e.stopPropagation() - form.handleSubmit({ - section: 'paymentInfo', - }) - }} + Submit and continue + + -
- + Submit and back to menu + + ) } ``` diff --git a/docs/framework/solid/guides/submission-handling.md b/docs/framework/solid/guides/submission-handling.md index 72dc7812d..0ff523e1c 100644 --- a/docs/framework/solid/guides/submission-handling.md +++ b/docs/framework/solid/guides/submission-handling.md @@ -5,8 +5,8 @@ title: Submission handling ## Passing additional data to submission handling -You may want to split your long form into multiple sections, for example, when creating a booking form. -To receive meta data in your `onSubmit` function, you can specify the `onSubmitMeta` property. +You may have multiple types of submission behaviour, for example, going back to another page or staying on the form. +You can accomplish this by specifying the `onSubmitMeta` property. This meta data will be passed to the `onSubmit` function. > Note: if `form.handleSubmit()` is called without metadata, it will use the provided default. @@ -14,63 +14,50 @@ To receive meta data in your `onSubmit` function, you can specify the `onSubmitM import { createForm } from '@tanstack/solid-form' type FormMeta = { - section: 'personalInfo' | 'paymentInfo' | null + submitAction: 'continue' | 'backToMenu' | null } // Metadata is not required to call form.handleSubmit(). // Specify what values to use as default if no meta is passed const defaultMeta: FormMeta = { - section: null, + submitAction: null, } export default function App() { const form = createForm(() => ({ defaultValues: { - personal: { - name: '', - email: '', - }, - payment: { - address: '', - }, + data: '', }, // Define what meta values to expect on submission onSubmitMeta: defaultMeta, onSubmit: async ({ value, meta }) => { // Do something with the values passed via handleSubmit - console.log(`Selected section - ${meta.section}`, value) + console.log(`Selected action - ${meta.submitAction}`, value) }, })) return ( - <> -
{ - e.preventDefault() - e.stopPropagation() - // Overwrites the default specified in onSubmitMeta - form.handleSubmit({ - section: 'personalInfo', - }) - }} + { + e.preventDefault() + e.stopPropagation() + }} + > + {/* ... */} + -
- -
{ - e.preventDefault() - e.stopPropagation() - form.handleSubmit({ - section: 'paymentInfo', - }) - }} + Submit and continue + + -
- + Submit and back to menu + + ) } ``` diff --git a/docs/framework/vue/guides/submission-handling.md b/docs/framework/vue/guides/submission-handling.md index e35b735e1..2976e0b85 100644 --- a/docs/framework/vue/guides/submission-handling.md +++ b/docs/framework/vue/guides/submission-handling.md @@ -5,8 +5,8 @@ title: Submission handling ## Passing additional data to submission handling -You may want to split your long form into multiple sections, for example, when creating a booking form. -To receive meta data in your `onSubmit` function, you can specify the `onSubmitMeta` property. +You may have multiple types of submission behaviour, for example, going back to another page or staying on the form. +You can accomplish this by specifying the `onSubmitMeta` property. This meta data will be passed to the `onSubmit` function. > Note: if `form.handleSubmit()` is called without metadata, it will use the provided default. @@ -15,52 +15,47 @@ To receive meta data in your `onSubmit` function, you can specify the `onSubmitM import { useForm } from '@tanstack/vue-form' type FormMeta = { - section: 'personalInfo' | 'paymentInfo' | null + submitAction: 'continue' | 'backToMenu' | null } // Metadata is not required to call form.handleSubmit(). // Specify what values to use as default if no meta is passed const defaultMeta: FormMeta = { - section: null, + submitAction: null, } const form = useForm({ defaultValues: { - personal: { - name: '', - email: '', - }, - payment: { - address: '', - }, + data: '', }, // Define what meta values to expect on submission onSubmitMeta: defaultMeta, onSubmit: async ({ value, meta }) => { // Do something with the values passed via handleSubmit - console.log(`Selected section - ${meta.section}`, value) + console.log(`Selected action - ${meta.submitAction}`, value) }, }) ``` From 9a84719a1f188ea16b9e3f7eab17fd33dce81e44 Mon Sep 17 00:00:00 2001 From: LeCarbonator <18158911+LeCarbonator@users.noreply.github.com> Date: Tue, 22 Apr 2025 10:27:42 +0200 Subject: [PATCH 8/8] Docs: Clean up redundant angular properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Pascal Küsgen --- docs/framework/angular/guides/submission-handling.md | 3 --- docs/framework/angular/guides/validation.md | 3 --- 2 files changed, 6 deletions(-) diff --git a/docs/framework/angular/guides/submission-handling.md b/docs/framework/angular/guides/submission-handling.md index a1c0a5cb4..e5fae2477 100644 --- a/docs/framework/angular/guides/submission-handling.md +++ b/docs/framework/angular/guides/submission-handling.md @@ -27,7 +27,6 @@ const defaultMeta: FormMeta = { @Component({ selector: 'app-root', - standalone: true, template: `