Skip to content

Commit 2149a92

Browse files
committed
feat(form-core): Add options to reset/replace all fields [FieldGroupApi]
1 parent cec6f48 commit 2149a92

File tree

2 files changed

+180
-1
lines changed

2 files changed

+180
-1
lines changed

packages/form-core/src/FieldGroupApi.ts

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,29 @@ export class FieldGroupApi<
397397
return this.form.deleteField(this.getFormFieldName(field))
398398
}
399399

400+
/**
401+
* Delete all fields that belong to this field group.
402+
*/
403+
deleteAllFields = () => {
404+
if (typeof this.fieldsMap === 'string') {
405+
const subFieldsToDelete = Object.keys(this.form.fieldInfo).filter((f) => {
406+
const fieldStr = this.fieldsMap.toString()
407+
return f !== fieldStr && f.startsWith(fieldStr)
408+
})
409+
410+
subFieldsToDelete.forEach((field) => {
411+
this.form.deleteField(field)
412+
})
413+
return
414+
}
415+
416+
const fieldsMap = this.fieldsMap as FieldsMap<TFormData, TFieldGroupData>
417+
418+
for (const key in fieldsMap) {
419+
this.deleteField(key)
420+
}
421+
}
422+
400423
/**
401424
* Pushes a value into an array field.
402425
*/
@@ -459,6 +482,20 @@ export class FieldGroupApi<
459482
)
460483
}
461484

485+
/**
486+
* Replaces all field values in this field group with the provided values.
487+
*/
488+
replaceAllFields = (fields: TFieldGroupData) => {
489+
for (const fieldName of Object.keys(
490+
fields as object,
491+
) as (keyof TFieldGroupData)[]) {
492+
this.setFieldValue(
493+
fieldName as unknown as DeepKeys<TFieldGroupData>,
494+
fields[fieldName] as never,
495+
)
496+
}
497+
}
498+
462499
/**
463500
* Removes a value from an array field at the specified index.
464501
*/
@@ -514,12 +551,33 @@ export class FieldGroupApi<
514551
}
515552

516553
/**
517-
* Resets the field value and meta to default state
554+
* Resets the field value and meta to default state.
518555
*/
519556
resetField = <TField extends DeepKeys<TFieldGroupData>>(field: TField) => {
520557
return this.form.resetField(this.getFormFieldName(field))
521558
}
522559

560+
/**
561+
* Resets all field values and meta within this field group.
562+
*/
563+
resetAllFields = () => {
564+
if (typeof this.fieldsMap === 'string') {
565+
const fieldsToReset = Object.keys(this.form.fieldInfo).filter((f) => {
566+
const fieldStr = this.fieldsMap.toString()
567+
return f !== fieldStr && f.startsWith(fieldStr)
568+
})
569+
570+
fieldsToReset.forEach((f) => this.form.resetField(f))
571+
return
572+
}
573+
574+
const fieldsMap = this.fieldsMap as FieldsMap<TFormData, TFieldGroupData>
575+
576+
for (const key in fieldsMap) {
577+
this.resetField(key)
578+
}
579+
}
580+
523581
validateAllFields = (cause: ValidationCause) =>
524582
this.form.validateAllFields(cause)
525583
}

packages/form-core/tests/FieldGroupApi.spec.ts

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,127 @@ describe('field group api', () => {
512512
expect(form.state.values.nested.field.name).toBeUndefined()
513513
})
514514

515+
it('should deleteAllFields for string field groups', () => {
516+
const defaultValues = {
517+
nested: {
518+
field: {
519+
name: 'hello',
520+
},
521+
},
522+
}
523+
524+
const form = new FormApi({
525+
defaultValues,
526+
})
527+
form.mount()
528+
529+
const field = new FieldApi({
530+
form,
531+
name: 'nested.field.name',
532+
})
533+
field.mount()
534+
535+
const group = new FieldGroupApi({
536+
defaultValues: { name: '' },
537+
form,
538+
fields: 'nested.field',
539+
})
540+
group.mount()
541+
542+
group.deleteAllFields()
543+
544+
expect(form.state.values.nested.field).toEqual({})
545+
})
546+
547+
it('should deleteAllFields for mapped field groups', () => {
548+
type FormVals = {
549+
a: string
550+
b: string
551+
}
552+
553+
const defaultValues: FormVals = { a: 'A', b: 'B' }
554+
const form = new FormApi({
555+
defaultValues,
556+
})
557+
form.mount()
558+
559+
const group = new FieldGroupApi({
560+
form,
561+
fields: {
562+
firstName: 'a',
563+
lastName: 'b',
564+
},
565+
defaultValues: { firstName: '', lastName: '' },
566+
})
567+
group.mount()
568+
569+
group.deleteAllFields()
570+
571+
expect(form.state.values.a).toBeUndefined()
572+
expect(form.state.values.b).toBeUndefined()
573+
})
574+
575+
it('should replaceAllFields for string field groups', () => {
576+
const defaultValues: FormValues = {
577+
name: '',
578+
age: 0,
579+
people: [],
580+
relatives: {
581+
father: {
582+
name: 'father',
583+
age: 10,
584+
},
585+
},
586+
}
587+
588+
const form = new FormApi({
589+
defaultValues,
590+
})
591+
form.mount()
592+
593+
const group = new FieldGroupApi({
594+
defaultValues: {} as Person,
595+
form,
596+
fields: 'relatives.father',
597+
})
598+
group.mount()
599+
600+
group.replaceAllFields({ name: 'New name', age: 99 })
601+
602+
expect(form.state.values.relatives.father).toEqual({
603+
name: 'New name',
604+
age: 99,
605+
})
606+
})
607+
608+
it('should replaceAllFields for mapped field groups', () => {
609+
type FormVals = {
610+
a: string
611+
b: string
612+
}
613+
614+
const defaultValues: FormVals = { a: 'A', b: 'B' }
615+
const form = new FormApi({
616+
defaultValues,
617+
})
618+
form.mount()
619+
620+
const group = new FieldGroupApi({
621+
form,
622+
fields: {
623+
firstName: 'a',
624+
lastName: 'b',
625+
},
626+
defaultValues: { firstName: '', lastName: '' },
627+
})
628+
group.mount()
629+
630+
group.replaceAllFields({ firstName: 'X', lastName: 'Y' })
631+
632+
expect(form.state.values.a).toBe('X')
633+
expect(form.state.values.b).toBe('Y')
634+
})
635+
515636
it('should forward array methods to the form', async () => {
516637
vi.useFakeTimers()
517638
const defaultValues = {

0 commit comments

Comments
 (0)