From d44ffa4888a92ff78837c9d23a303d583b7a6d13 Mon Sep 17 00:00:00 2001 From: Michael Aziz Date: Mon, 22 Dec 2025 11:37:18 +0200 Subject: [PATCH] [FIX] form controller cascades fix. --- lib/src/form/src/controller.dart | 113 ++++++++++++++++++++++++------- 1 file changed, 90 insertions(+), 23 deletions(-) diff --git a/lib/src/form/src/controller.dart b/lib/src/form/src/controller.dart index ae6cfa3..5ba0329 100644 --- a/lib/src/form/src/controller.dart +++ b/lib/src/form/src/controller.dart @@ -308,9 +308,7 @@ class AstromicFormController extends FormController { : standeredGroupFormat(parentPrefix, groupIndex.toString(), subGroup.id); // Add to parentPrefix only once // Initialize subgroup controllers recursively - for (int subIndex = 0; subIndex < subgroupInitialCount; subIndex++) { - _initializeGroupControllersRecursively(subGroup, subgroupInitialCount, parentPrefix: subgroupPrefix); - } + _initializeGroupControllersRecursively(subGroup, subgroupInitialCount, parentPrefix: subgroupPrefix); } } } @@ -426,78 +424,147 @@ class AstromicFormController extends FormController { return p.trim(); } + void _copyInstance(String groupID, int fromIndex, int toIndex, Map? parents) { + log('_copyInstance: $groupID from $fromIndex to $toIndex, parents=$parents'); + FormGroupStructure? structure = getGroupStructure(groupID); + assert(structure != null); + + String prefix = parents?.entries.map((MapEntry e) => standeredGroupFormat(e.key, e.value.toString(), null)).join('-') ?? ''; + String composedID = prefix.isNotEmpty ? '$prefix-$groupID' : groupID; + + // Copy fields + for (String field in structure!.fields) { + String fromID = standeredGroupFormat(composedID, fromIndex.toString(), field); + String toID = standeredGroupFormat(composedID, toIndex.toString(), field); + String val = tryValue(fromID) ?? ''; + log('Copying field $fromID ($val) to $toID'); + set(toID, val); + } + + // Copy values + for (String value in structure.values ?? []) { + String fromID = standeredGroupFormat(composedID, fromIndex.toString(), value); + String toID = standeredGroupFormat(composedID, toIndex.toString(), value); + dynamic val = getValue(fromID); + log('Copying value $fromID ($val) to $toID'); + setValue(toID, val); + set(toID, tryValue(fromID) ?? ''); + } + + // Recursively copy subgroups + if (structure.subGroups != null) { + for ((int, FormGroupStructure) sg in structure.subGroups!) { + int sgCount = getInstanceCount(sg.$2.id, parents: {...?parents, groupID: fromIndex}); + log('Subgroup ${sg.$2.id} has $sgCount instances'); + for (int j = 0; j < sgCount; j++) { + _copyInstance(sg.$2.id, j, j, {...?parents, groupID: toIndex}); + } + } + } + } + + void _removeInstance(String groupID, int index, Map? parents) { + log('_removeInstance: $groupID index $index, parents=$parents'); + _checkSubgroups(groupID, parents: {...?parents, groupID: index}); + FormGroupStructure? structure = getGroupStructure(groupID); + String prefix = parents?.entries.map((MapEntry e) => standeredGroupFormat(e.key, e.value.toString(), null)).join('-') ?? ''; + String composedID = prefix.isNotEmpty ? '$prefix-$groupID' : groupID; + _removeGroupControllers(composedID, [index], structure!.fields, structure.values ?? []); + } + void _removeGroupControllers(String composedID, List indecies, List fields, List values, {bool switchValuesFirst = false}) { + log('_removeGroupControllers: composedID=$composedID, indecies=$indecies, switchValuesFirst=$switchValuesFirst'); for (int i in indecies) { + log('Processing index $i'); for (String fieldID in fields) { String p = standeredGroupFormat(composedID, (switchValuesFirst ? (i + 1) : i).toString(), fieldID); String k = standeredGroupFormat(composedID, i.toString(), fieldID); + log('Field: k=$k, p=$p'); if (switchValuesFirst) { - set(k, tryValue(p) ?? ''); + String val = tryValue(p) ?? ''; + log('Setting $k to $val from $p'); + set(k, val); } + log('Removing controller $p'); removeController(p); } for (String valueID in values) { String p = standeredGroupFormat(composedID, (switchValuesFirst ? (i + 1) : i).toString(), valueID); String k = standeredGroupFormat(composedID, i.toString(), valueID); + log('Value: k=$k, p=$p'); if (switchValuesFirst) { - set(k, tryValue(p) ?? ''); - setValue(k, getValue(p) ?? ''); + String val = tryValue(p) ?? ''; + log('Setting $k to $val from $p'); + set(k, val); + dynamic hostedVal = getValue(p); + log('Setting hosted value $k to $hostedVal from $p'); + setValue(k, hostedVal); } + log('Removing hosted value $p'); removeValue(p); set(p, ''); + log('Removing controller $p'); removeController(p); } } } void _checkSubgroups(String groupID, {Map? parents}) { + log('_checkSubgroups: groupID=$groupID, parents=$parents'); // Get the group structure and check it's subGroups FormGroupStructure? structure = getGroupStructure(groupID); if (structure!.subGroups != null && structure.subGroups!.isNotEmpty) { for ((int, FormGroupStructure) sg in structure.subGroups!) { - // Get the SubGroup instances Count and prefix it. - int subgroupCount = getInstanceCount(sg.$2.id, parents: {if (parents != null) parents.entries.first.key: parents.entries.first.value + 1}); + log('Processing subgroup ${sg.$2.id}'); + // Get the SubGroup instances Count under this parent instance + int subgroupCount = getInstanceCount(sg.$2.id, parents: parents); + log('Subgroup count: $subgroupCount'); String prefix = parents?.entries.map((MapEntry parentEntry) => standeredGroupFormat(parentEntry.key, parentEntry.value.toString(), null)).join('-') ?? ''; + log('Prefix for subgroup: $prefix'); - // Recurse through subGroups to find the latest. + // Recurse through subGroups to remove their sub-subgroups for (int sgInstance = 0; sgInstance < subgroupCount; sgInstance++) { + log('Recursing for sgInstance $sgInstance'); _checkSubgroups( sg.$2.id, parents: { ...?parents, - ...{sg.$2.id: sgInstance} + sg.$2.id: sgInstance }, ); } // Remove controllers for these subgroups. - _removeGroupControllers(prefix.isNotEmpty ? '$prefix-${sg.$2.id}' : sg.$2.id, List.generate(subgroupCount, (int ii) => ii), sg.$2.fields, sg.$2.values ?? []); + String composedID = prefix.isNotEmpty ? '$prefix-${sg.$2.id}' : sg.$2.id; + log('Removing controllers for $composedID, indices 0 to ${subgroupCount-1}'); + _removeGroupControllers(composedID, List.generate(subgroupCount, (int ii) => ii), sg.$2.fields, sg.$2.values ?? []); } } } /// Remove an instance from the group void removeInstanceFromGroup(String targetGroupID, int indexToRemove, {Map? parents}) { - // Get tha main targeted group's structure + log('Removing instance $indexToRemove from group $targetGroupID with parents $parents'); + // Get the main targeted group's structure FormGroupStructure? targetedGroupStructure = getGroupStructure(targetGroupID); assert(targetedGroupStructure != null, 'The Group $targetGroupID doesn\'t seem to be found, are you sure you initialized it?'); - // Get tha main targeted group's count + // Get the main targeted group's count int targetedGroupCount = getInstanceCount(targetGroupID, parents: parents); + log('Targeted group count: $targetedGroupCount'); assert(indexToRemove < targetedGroupCount, 'The index to remove is larger than the whole instances count. ($indexToRemove , $targetedGroupCount)'); - // Loop through the subGroups and remove their controllers. - - // Prefix the main targeted ID and remove it's controllers. - String prefix = parents?.entries.map((MapEntry parentEntry) => standeredGroupFormat(parentEntry.key, parentEntry.value.toString(), null)).join('-') ?? ''; if (indexToRemove == (targetedGroupCount - 1)) { + log('Removing last item'); // Last Item in the group, Remove directly. - _checkSubgroups(targetGroupID, parents: {...?parents, targetGroupID: indexToRemove}); - _removeGroupControllers(prefix.isNotEmpty ? '$prefix-$targetGroupID' : targetGroupID, [indexToRemove], targetedGroupStructure!.fields, targetedGroupStructure.values ?? []); + _removeInstance(targetGroupID, indexToRemove, parents); } else { - //TODO - Has an issue with transferring subGroups... - _checkSubgroups(targetGroupID, parents: {...?parents, targetGroupID: indexToRemove}); - _removeGroupControllers(prefix.isNotEmpty ? '$prefix-$targetGroupID' : targetGroupID, [indexToRemove], targetedGroupStructure!.fields, targetedGroupStructure.values ?? [], - switchValuesFirst: true); + log('Removing middle item, shifting'); + // Shift all subsequent instances down + for (int i = indexToRemove; i < targetedGroupCount - 1; i++) { + _copyInstance(targetGroupID, i + 1, i, parents); + } + // Then remove the last instance + _removeInstance(targetGroupID, targetedGroupCount - 1, parents); } }