Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions packages/form-core/src/FieldApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,24 @@ export type FieldAsyncValidateOrFn<
TData
>

/**
* @private
*/
export type FieldListenerFn<
TParentData,
TName extends DeepKeys<TParentData>,
TFieldValidator extends
| Validator<DeepValue<TParentData, TName>, unknown>
| undefined = undefined,
TFormValidator extends
| Validator<TParentData, unknown>
| undefined = undefined,
TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,
> = (props: {
value: TData
fieldApi: FieldApi<TParentData, TName, TFieldValidator, TFormValidator, TData>
}) => void

export interface FieldValidators<
TParentData,
TName extends DeepKeys<TParentData>,
Expand Down Expand Up @@ -251,6 +269,40 @@ export interface FieldValidators<
>
}

export interface FieldListeners<
TParentData,
TName extends DeepKeys<TParentData>,
TFieldValidator extends
| Validator<DeepValue<TParentData, TName>, unknown>
| undefined = undefined,
TFormValidator extends
| Validator<TParentData, unknown>
| undefined = undefined,
TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,
> {
onChange?: FieldListenerFn<
TParentData,
TName,
TFieldValidator,
TFormValidator,
TData
>
onHandleChange?: FieldListenerFn<
TParentData,
TName,
TFieldValidator,
TFormValidator,
TData
>
Comment on lines +283 to +296
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not convinced we need a different function for programmatic vs user input. If the user needs this distinction, they can do so outside of our listeners

It also opens a naming can of worms and adds another one when we need an onBlurChange kinda deal

onBlur?: FieldListenerFn<
TParentData,
TName,
TFieldValidator,
TFormValidator,
TData
>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want an onSubmit and onMount here as well?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it would give more versatility to the feature, and maybe onSubmit should allow for transformation before sending out the data ?

}

/**
* An object type representing the options for a field in a form.
*/
Expand Down Expand Up @@ -299,6 +351,16 @@ export interface FieldOptions<
* An optional object with default metadata for the field.
*/
defaultMeta?: Partial<FieldMeta>
/**
* A list of listeners which attach to the corresponding events
*/
listeners?: FieldListeners<
TParentData,
TName,
TFieldValidator,
TFormValidator,
TData
>
}

/**
Expand Down Expand Up @@ -479,6 +541,11 @@ export class FieldApi<

this.prevState = state
this.state = state

this.options.listeners?.onChange?.({
value: state.value,
fieldApi: this,
})
},
},
)
Expand Down Expand Up @@ -949,6 +1016,11 @@ export class FieldApi<
*/
handleChange = (updater: Updater<TData>) => {
this.setValue(updater, { touch: true })

this.options.listeners?.onHandleChange?.({
value: this.state.value,
fieldApi: this,
})
}

/**
Expand All @@ -961,6 +1033,11 @@ export class FieldApi<
this.validate('change')
}
this.validate('blur')

this.options.listeners?.onBlur?.({
value: this.state.value,
fieldApi: this,
})
}
}

Expand Down
72 changes: 72 additions & 0 deletions packages/form-core/tests/FieldApi.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,78 @@ describe('field api', () => {
})
})

it('should run listener on handleChange', () => {
const form = new FormApi({
defaultValues: {
name: 'test',
},
})

let triggered!: string
const field = new FieldApi({
form,
name: 'name',
listeners: {
onHandleChange: ({ value }) => {
triggered = value
},
},
})

field.mount()

field.handleChange('other')
expect(triggered).toStrictEqual('other')
})

it('should run listener onChange', () => {
const form = new FormApi({
defaultValues: {
name: 'test',
},
})

let triggered!: string
const field = new FieldApi({
form,
name: 'name',
listeners: {
onChange: ({ value }) => {
triggered = value
},
},
})

field.mount()

field.setValue('other', { touch: true })
expect(triggered).toStrictEqual('other')
})

it('should run listener onBlur', () => {
const form = new FormApi({
defaultValues: {
name: 'test',
},
})

let triggered!: string
const field = new FieldApi({
form,
name: 'name',
listeners: {
onBlur: ({ value }) => {
triggered = value
},
},
})

field.mount()

field.handleBlur()
expect(triggered).toStrictEqual('test')
})

it('should contain multiple errors when running validation onBlur and onChange', () => {
const form = new FormApi({
defaultValues: {
Expand Down