From e7212843e988b2c2f6ddcfd31f2d16560192b348 Mon Sep 17 00:00:00 2001 From: Jukka Lipka <3710455+jlipka@users.noreply.github.com> Date: Mon, 6 Oct 2025 10:40:45 +0200 Subject: [PATCH] fix: improve handling of controls in form array - Refactored control retrieval logic by replacing `getControlOfGroup` with direct use of `control.get` without using a startingIndex - Added `moveFormControlToPosition` method to handle control positioning during drag-and-drop operations. Ensured controls remain correctly linked to their respective groups --- .../dynamic-form-array.component.html | 2 +- .../dynamic-form-array.component.spec.ts | 33 ++++++++++++++++ .../dynamic-form-array.component.ts | 39 +++++++++++-------- 3 files changed, 56 insertions(+), 18 deletions(-) diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/array-group/dynamic-form-array.component.html b/src/app/shared/form/builder/ds-dynamic-form-ui/models/array-group/dynamic-form-array.component.html index 5c6d50d6ab8..7141ba0748b 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/array-group/dynamic-form-array.component.html +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/array-group/dynamic-form-array.component.html @@ -35,7 +35,7 @@ [formGroup]="group" [formModel]="formModel" [context]="groupModel" - [group]="getControlOfGroup(groupModel)" + [group]="control.get([groupModel.index])" [hidden]="_model.hidden" [class.d-none]="_model.hidden" [layout]="formLayout" diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/array-group/dynamic-form-array.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/array-group/dynamic-form-array.component.spec.ts index 2367615d22d..1561b1447c4 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/array-group/dynamic-form-array.component.spec.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/array-group/dynamic-form-array.component.spec.ts @@ -159,4 +159,37 @@ describe('DsDynamicFormArrayComponent', () => { expect(component.elementBeingSorted).toBeNull(); expect(component.elementBeingSortedStartingIndex).toBeNull(); }); + + describe('moveFormControlToPosition', () => { + it('should move form control from one position to another', () => { + const formArray = component.control as any; + const initialControls = formArray.controls.map((ctrl: any) => ctrl); + const movedControl = initialControls[1]; + + // Move control from index 1 to index 3 + (component as any).moveFormControlToPosition(1, 3); + + expect(formArray.at(3)).toBe(movedControl); + expect(formArray.length).toBe(5); + }); + + it('should preserve form control values after move', () => { + const formArray = component.control as any; + + // Set actual values to the form controls + formArray.at(0).patchValue({ testFormRowArrayGroupInput: 'Author 1' }); + formArray.at(1).patchValue({ testFormRowArrayGroupInput: 'Author 2' }); + formArray.at(2).patchValue({ testFormRowArrayGroupInput: 'Author 3' }); + formArray.at(3).patchValue({ testFormRowArrayGroupInput: 'Author 4' }); + formArray.at(4).patchValue({ testFormRowArrayGroupInput: 'Author 5' }); + + (component as any).moveFormControlToPosition(1, 3); + + expect(formArray.at(0).value.testFormRowArrayGroupInput).toBe('Author 1'); + expect(formArray.at(1).value.testFormRowArrayGroupInput).toBe('Author 3'); + expect(formArray.at(2).value.testFormRowArrayGroupInput).toBe('Author 4'); + expect(formArray.at(3).value.testFormRowArrayGroupInput).toBe('Author 2'); + expect(formArray.at(4).value.testFormRowArrayGroupInput).toBe('Author 5'); + }); + }); }); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/array-group/dynamic-form-array.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/array-group/dynamic-form-array.component.ts index 34cac3f6db1..a677da88a3e 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/array-group/dynamic-form-array.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/array-group/dynamic-form-array.component.ts @@ -88,15 +88,16 @@ export class DsDynamicFormArrayComponent extends DynamicFormArrayComponent { } moveSelection(event: CdkDragDrop) { + const prevIndex = event.previousIndex; + const index = event.currentIndex; // prevent propagating events generated releasing on the same position - if (event.previousIndex === event.currentIndex) { + if (prevIndex === index) { return; } - this.model.moveGroup(event.previousIndex, event.currentIndex - event.previousIndex); - const prevIndex = event.previousIndex; - const index = event.currentIndex; + this.model.moveGroup(prevIndex, index - prevIndex); + this.moveFormControlToPosition(prevIndex, index); if (hasValue(this.model.groups[index]) && hasValue((this.control as any).controls[index])) { this.onCustomEvent({ @@ -124,19 +125,6 @@ export class DsDynamicFormArrayComponent extends DynamicFormArrayComponent { return this.model.groups.length === 1 || !this.model.isDraggable; } - /** - * Gets the control of the specified group model. It adds the startingIndex property to the group model if it does not - * already have it. This ensures that the controls are always linked to the correct group model. - * @param groupModel The group model to get the control for. - * @returns The form control of the specified group model. - */ - getControlOfGroup(groupModel: any) { - if (!groupModel.hasOwnProperty('startingIndex')) { - groupModel.startingIndex = groupModel.index; - } - return this.control.get([groupModel.startingIndex]); - } - /** * Toggles the keyboard drag and drop feature for the given sortable element. * @param event @@ -199,6 +187,7 @@ export class DsDynamicFormArrayComponent extends DynamicFormArrayComponent { if (this.elementBeingSorted) { this.model.moveGroup(idx, newIndex - idx); + this.moveFormControlToPosition(idx, newIndex); if (hasValue(this.model.groups[newIndex]) && hasValue((this.control as any).controls[newIndex])) { this.onCustomEvent({ previousIndex: idx, @@ -227,6 +216,7 @@ export class DsDynamicFormArrayComponent extends DynamicFormArrayComponent { cancelKeyboardDragAndDrop(sortableElement: HTMLDivElement, index: number, length: number) { this.model.moveGroup(index, this.elementBeingSortedStartingIndex - index); + this.moveFormControlToPosition(index, this.elementBeingSortedStartingIndex); if (hasValue(this.model.groups[this.elementBeingSortedStartingIndex]) && hasValue((this.control as any).controls[this.elementBeingSortedStartingIndex])) { this.onCustomEvent({ previousIndex: index, @@ -281,4 +271,19 @@ export class DsDynamicFormArrayComponent extends DynamicFormArrayComponent { })); } } + + private moveFormControlToPosition(fromIndex: number, toIndex: number) { + if (!hasValue(fromIndex) || !hasValue(toIndex)) { + return; + } + + const formArray = this.control as any; + if (formArray && formArray.controls) { + const movedControl = formArray.at(fromIndex); + if (movedControl) { + formArray.removeAt(fromIndex,{ emitEvent: false }); + formArray.insert(toIndex, movedControl, { emitEvent: false }); + } + } + } }