Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): add field listeners #1032

Merged
merged 15 commits into from
Nov 25, 2024
4 changes: 4 additions & 0 deletions docs/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@
"label": "Linked Fields",
"to": "framework/react/guides/linked-fields"
},
{
"label": "Listeners",
"to": "framework/react/guides/listeners"
},
{
"label": "UI Libraries",
"to": "framework/react/guides/ui-libraries"
Expand Down
35 changes: 35 additions & 0 deletions docs/framework/angular/guides/basic-concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,41 @@ class AppComponent {
}
```

## Listeners

`@tanstack/angular-form` allows you to react to specific triggers and "listen" to them to dispatch side effects.

Example:

```angular-ts
@Component({
selector: 'app-root',
standalone: true,
imports: [TanStackField],
template: `
<ng-container
[tanstackField]="form"
name="country"
[listeners]="{
onChange: onCountryChange
}"
#country="field"
></ng-container>
`,
})

...

onCountryChange: FieldListenerFn<any, any, any, any, string> = ({
value,
}) => {
console.log(`Country changed to: ${value}, resetting province`)
this.form.setFieldValue('province', '')
}
```

More information can be found at [Listeners](./listeners.md)

## Array Fields

Array fields allow you to manage a list of values within a form, such as a list of hobbies. You can create an array field using the `tanstackField` directive.
Expand Down
61 changes: 61 additions & 0 deletions docs/framework/angular/guides/listeners.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
id: listeners
title: Side effects for event triggers
---

For situations where you want to "affect" or "react" to triggers, there's the listener API. For example, if you, as the developer, want to reset a form field as a result of another field changing, you would use the listener API.

Imagine the following user flow:

- User selects a country from a drop-down.
- User then selects a province from another drop-down.
- User changes the selected country to a different one.

In this example, when the user changes the country, the selected province needs to be reset as it's no longer valid. With the listener API, we can subscribe to the onChange event and dispatch a reset to the field "province" when the listener is fired.

Events that can be "listened" to are:

- onChange
- onBlur
- onMount
- onSubmit

```angular-ts
@Component({
selector: 'app-root',
standalone: true,
imports: [TanStackField],
template: `
<ng-container
[tanstackField]="form"
name="country"
[listeners]="{
onChange: onCountryChange
}"
#country="field"
></ng-container>

<ng-container
[tanstackField]="form"
name="province"
#province="field"
></ng-container>
`,
})

export class AppComponent {
form = injectForm({
defaultValues: {
country: '',
province: '',
},
})

onCountryChange: FieldListenerFn<any, any, any, any, string> = ({
value,
}) => {
console.log(`Country changed to: ${value}, resetting province`)
this.form.setFieldValue('province', '')
}
}
```
44 changes: 31 additions & 13 deletions docs/framework/angular/reference/classes/tanstackfield.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ api: FieldApi<TParentData, TName, TFieldValidator, TFormValidator, TData>;

#### Defined in

[tanstack-field.directive.ts:58](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L58)
[tanstack-field.directive.ts:62](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L62)

***

Expand All @@ -64,7 +64,7 @@ If `true`, always run async validation, even if there are errors emitted during

#### Defined in

[tanstack-field.directive.ts:47](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L47)
[tanstack-field.directive.ts:48](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L48)

***

Expand All @@ -82,7 +82,7 @@ The default time to debounce async validation if there is not a more specific de

#### Defined in

[tanstack-field.directive.ts:46](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L46)
[tanstack-field.directive.ts:47](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L47)

***

Expand All @@ -100,7 +100,7 @@ An optional object with default metadata for the field.

#### Defined in

[tanstack-field.directive.ts:56](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L56)
[tanstack-field.directive.ts:60](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L60)

***

Expand All @@ -118,7 +118,25 @@ An optional default value for the field.

#### Defined in

[tanstack-field.directive.ts:45](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L45)
[tanstack-field.directive.ts:46](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L46)

***

### listeners?

```ts
optional listeners: NoInfer<FieldListeners<TParentData, TName, TFieldValidator, TFormValidator, TData>>;
```

A list of listeners which attach to the corresponding events

#### Implementation of

`FieldOptions.listeners`

#### Defined in

[tanstack-field.directive.ts:57](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L57)

***

Expand All @@ -136,7 +154,7 @@ The field name. The type will be `DeepKeys<TParentData>` to ensure your name is

#### Defined in

[tanstack-field.directive.ts:41](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L41)
[tanstack-field.directive.ts:42](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L42)

***

Expand All @@ -148,7 +166,7 @@ tanstackField: FormApi<TParentData, TFormValidator>;

#### Defined in

[tanstack-field.directive.ts:49](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L49)
[tanstack-field.directive.ts:50](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L50)

***

Expand All @@ -164,7 +182,7 @@ optional unmount: () => void;

#### Defined in

[tanstack-field.directive.ts:73](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L73)
[tanstack-field.directive.ts:78](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L78)

***

Expand All @@ -182,7 +200,7 @@ A validator provided by an extension, like `yupValidator` from `@tanstack/yup-fo

#### Defined in

[tanstack-field.directive.ts:48](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L48)
[tanstack-field.directive.ts:49](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L49)

***

Expand All @@ -200,7 +218,7 @@ A list of validators to pass to the field

#### Defined in

[tanstack-field.directive.ts:53](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L53)
[tanstack-field.directive.ts:54](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L54)

## Methods

Expand All @@ -225,7 +243,7 @@ children are checked.

#### Defined in

[tanstack-field.directive.ts:85](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L85)
[tanstack-field.directive.ts:90](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L90)

***

Expand All @@ -248,7 +266,7 @@ before a directive, pipe, or service instance is destroyed.

#### Defined in

[tanstack-field.directive.ts:81](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L81)
[tanstack-field.directive.ts:86](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L86)

***

Expand All @@ -274,4 +292,4 @@ It is invoked only once when the directive is instantiated.

#### Defined in

[tanstack-field.directive.ts:75](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L75)
[tanstack-field.directive.ts:80](https://github.com/TanStack/form/blob/main/packages/angular-form/src/tanstack-field.directive.ts#L80)
20 changes: 20 additions & 0 deletions docs/framework/react/guides/basic-concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,26 @@ const firstName = form.useStore((state) => state.values.firstName)

Note: The usage of the `form.useField` hook to achieve reactivity is discouraged since it is designed to be used thoughtfully within the `form.Field` component. You might want to use `form.useStore` instead.

## Listeners

`@tanstack/react-form` allows you to react to specific triggers and "listen" to them to dispatch side effects.

Example:

```tsx
<form.Field
name="country"
listeners={{
onChange: ({ value }) => {
console.log(`Country changed to: ${value}, resetting province`)
form.setFieldValue('province', '')
}
}}
/>
```

harry-whorlow marked this conversation as resolved.
Show resolved Hide resolved
More information can be found at [Listeners](./listeners.md)

## Array Fields

Array fields allow you to manage a list of values within a form, such as a list of hobbies. You can create an array field using the `form.Field` component with the `mode="array"` prop.
Expand Down
67 changes: 67 additions & 0 deletions docs/framework/react/guides/listeners.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
---
id: listeners
title: Side effects for event triggers
---

For situations where you want to "affect" or "react" to triggers, there's the listener API. For example, if you, as the developer, want to reset a form field as a result of another field changing, you would use the listener API.

Imagine the following user flow:

- User selects a country from a drop-down.
- User then selects a province from another drop-down.
- User changes the selected country to a different one.

In this example, when the user changes the country, the selected province needs to be reset as it's no longer valid. With the listener API, we can subscribe to the onChange event and dispatch a reset to the field "province" when the listener is fired.

Events that can be "listened" to are:

- onChange
- onBlur
- onMount
- onSubmit

```tsx
function App() {
const form = useForm({
defaultValues: {
country: '',
province: '',
},
// ...
})

return (
<div>
<form.Field name="country">
{(field) => (
<label>
<div>Country</div>
<input
value={field.state.value}
onChange={(e) => field.handleChange(e.target.value)}
listeners={{
onChange: ({ value }) => {
console.log(`Country changed to: ${value}, resetting province`)
form.setFieldValue('province', '')
}
}}
/>
</label>
)}
</form.Field>

<form.Field name="province">
{(field) => (
<label>
<div>Province</div>
<input
value={field.state.value}
onChange={(e) => field.handleChange(e.target.value)}
/>
</label>
)}
</form.Field>
</div>
)
}
```
29 changes: 29 additions & 0 deletions docs/framework/vue/guides/basic-concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,35 @@ const firstName = form.useStore((state) => state.values.firstName)
</template>
```

## Listeners

`@tanstack/vue-form` allows you to react to specific triggers and "listen" to them to dispatch side effects.

Example:

```vue
<template>
<form.Field
name="country"
:listeners="{
onChange: ({ value }) => {
console.log(`Country changed to: ${value}, resetting province`)
form.setFieldValue('province', '')
}
}"
>
<template v-slot="{ field }">
<input
:value="field.state.value"
@input="(e) => field.handleChange(e.target.value)"
/>
</template>
</form.Field>
</template>
```

More information can be found at [Listeners](./listeners.md)

Note: The usage of the `form.useField` method to achieve reactivity is discouraged since it is designed to be used thoughtfully within the `form.Field` component. You might want to use `form.useStore` instead.

## Array Fields
Expand Down
Loading