diff --git a/packages/form-core/src/FieldApi.ts b/packages/form-core/src/FieldApi.ts index dac3c2571..33fcd8cc5 100644 --- a/packages/form-core/src/FieldApi.ts +++ b/packages/form-core/src/FieldApi.ts @@ -1313,6 +1313,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 258a540da..a048c21d2 100644 --- a/packages/form-core/src/FormApi.ts +++ b/packages/form-core/src/FormApi.ts @@ -2125,6 +2125,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 c10c3fa9c..c700d5e88 100644 --- a/packages/form-core/tests/FieldApi.spec.ts +++ b/packages/form-core/tests/FieldApi.spec.ts @@ -1346,6 +1346,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 09fbf87c4..79796ac78 100644 --- a/packages/form-core/tests/FormApi.spec.ts +++ b/packages/form-core/tests/FormApi.spec.ts @@ -3267,6 +3267,28 @@ 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', + }, + ] + + 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([]) + }) }) it('should reset the errorSourceMap for the field when the form is reset', () => {