diff --git a/packages/form-core/src/FieldApi.ts b/packages/form-core/src/FieldApi.ts index a95fd5515..ce758cf4c 100644 --- a/packages/form-core/src/FieldApi.ts +++ b/packages/form-core/src/FieldApi.ts @@ -1288,6 +1288,15 @@ export class FieldApi< this.triggerOnChangeListener() } + /** + * Clear all values from the array. + */ + clearValues = (opts?: UpdateMetaOptions) => { + this.form.clearFieldValues(this.name, opts) + + this.triggerOnChangeListener() + } + /** * @private */ diff --git a/packages/form-core/src/FormApi.ts b/packages/form-core/src/FormApi.ts index e95b295f2..e0bffaee2 100644 --- a/packages/form-core/src/FormApi.ts +++ b/packages/form-core/src/FormApi.ts @@ -1962,6 +1962,32 @@ export class FormApi< this.validateField(`${field}[${index2}]` as DeepKeys, 'change') } + /** + * Clear all values within an array field. + */ + clearFieldValues = >( + field: TField, + opts?: UpdateMetaOptions, + ) => { + const fieldValue = this.getFieldValue(field) + + const lastIndex = Array.isArray(fieldValue) + ? Math.max((fieldValue as unknown[]).length - 1, 0) + : null + + this.setFieldValue(field, [] as any, opts) + + if (lastIndex !== null) { + for (let i = 0; i <= lastIndex; i++) { + const fieldKey = `${field}[${i}]` + this.deleteField(fieldKey as never) + } + } + + // validate array change + this.validateField(field, 'change') + } + /** * Resets the field value and meta to default state */ diff --git a/packages/form-core/tests/FieldApi.spec.ts b/packages/form-core/tests/FieldApi.spec.ts index 4ed38a623..94e1a8165 100644 --- a/packages/form-core/tests/FieldApi.spec.ts +++ b/packages/form-core/tests/FieldApi.spec.ts @@ -1195,6 +1195,28 @@ describe('field api', () => { field.moveValue(0, 1) expect(arr).toStrictEqual(['middle', 'end', 'start']) + + field.clearValues() + expect(arr).toStrictEqual([]) + }) + + it('should not break when clearValues is called on a non-array field', () => { + const form = new FormApi({ + defaultValues: { + name: 'foo', + }, + }) + + form.mount() + + const field = new FieldApi({ + form, + name: 'name', + }) + + field.mount() + + expect(() => field.clearValues()).not.toThrow() }) it('should reset the form on a listener', () => { diff --git a/packages/form-core/tests/FormApi.spec.ts b/packages/form-core/tests/FormApi.spec.ts index 79ca9f797..5b9b96322 100644 --- a/packages/form-core/tests/FormApi.spec.ts +++ b/packages/form-core/tests/FormApi.spec.ts @@ -3015,4 +3015,26 @@ describe('form api', () => { form.parseValuesWithSchemaAsync(z.any()) }).not.toThrowError() }) + + it('should delete fields when resetting an array field to an empty array', () => { + const employees = [ + { + firstName: 'Darcy', + }, + ] as const + + const form = new FormApi({ + defaultValues: { + employees, + }, + }) + form.mount() + + form.clearFieldValues('employees') + + expect(form.getFieldValue('employees')).toEqual([]) + expect(form.getFieldValue(`employees[0]`)).toBeUndefined() + expect(form.getFieldMeta(`employees[0]`)).toBeUndefined() + expect(form.state.values.employees).toStrictEqual([]) + }) })