diff --git a/ext/afform/core/Civi/Api4/Action/Afform/AbstractProcessor.php b/ext/afform/core/Civi/Api4/Action/Afform/AbstractProcessor.php
index fc0451bf34ee..df07e413f0e3 100644
--- a/ext/afform/core/Civi/Api4/Action/Afform/AbstractProcessor.php
+++ b/ext/afform/core/Civi/Api4/Action/Afform/AbstractProcessor.php
@@ -102,7 +102,13 @@ protected function loadEntities() {
public function loadEntity(array $entity, array $ids) {
// Limit number of records based on af-repeat settings
// If 'min' is set then it is repeatable, and max will either be a number or NULL for unlimited.
- $ids = array_slice($ids, 0, isset($entity['min']) ? $entity['max'] : 1);
+ if (isset($entity['min']) && isset($entity['max'])) {
+ foreach (array_keys($ids) as $index) {
+ if ($index >= $entity['max']) {
+ unset($ids[$index]);
+ }
+ }
+ }
$api4 = $this->_formDataModel->getSecureApi4($entity['name']);
$idField = CoreUtil::getIdFieldName($entity['type']);
diff --git a/ext/afform/core/ang/af/afForm.component.js b/ext/afform/core/ang/af/afForm.component.js
index d711b00b8aea..3208ff8b1351 100644
--- a/ext/afform/core/ang/af/afForm.component.js
+++ b/ext/afform/core/ang/af/afForm.component.js
@@ -43,11 +43,11 @@
// With no arguments this will prefill the entire form based on url args
// With selectedEntity, selectedIndex & selectedId provided this will prefill a single entity
this.loadData = function(selectedEntity, selectedIndex, selectedId, selectedField) {
- var toLoad = 0,
- params = {name: ctrl.getFormMeta().name, args: {}};
+ let toLoad = false;
+ const params = {name: ctrl.getFormMeta().name, args: {}};
// Load single entity
if (selectedEntity) {
- toLoad = 1;
+ toLoad = !!selectedId;
params.matchField = selectedField;
params.args[selectedEntity] = {};
params.args[selectedEntity][selectedIndex] = selectedId;
@@ -57,7 +57,7 @@
args = _.assign({}, $scope.$parent.routeParams || {}, $scope.$parent.options || {});
_.each(schema, function (entity, entityName) {
if (args[entityName] || entity.actions.update) {
- toLoad++;
+ toLoad = true;
}
if (args[entityName] && typeof args[entityName] === 'string') {
args[entityName] = args[entityName].split(',');
@@ -67,19 +67,23 @@
}
if (toLoad) {
crmApi4('Afform', 'prefill', params)
- .then(function(result) {
- _.each(result, function(item) {
- data[item.name] = data[item.name] || {};
- _.extend(data[item.name], item.values, schema[item.name].data || {});
+ .then((result) => {
+ result.forEach((item) => {
+ // Use _.each() because item.values could be cast as an object if array keys are not sequential
+ _.each(item.values, (values, index) => {
+ data[item.name][index].joins = {};
+ angular.merge(data[item.name][index], values, {fields: _.cloneDeep(schema[item.name].data || {})});
+ });
});
});
}
// Clear existing contact selection
else if (selectedEntity) {
- data[selectedEntity][selectedIndex].fields = {};
- if (data[selectedEntity][selectedIndex].joins) {
- data[selectedEntity][selectedIndex].joins = {};
- }
+ // Delete object keys without breaking object references
+ Object.keys(data[selectedEntity][selectedIndex].fields).forEach(key => delete data[selectedEntity][selectedIndex].fields[key]);
+ // Fill pre-set values
+ angular.merge(data[selectedEntity][selectedIndex].fields, _.cloneDeep(schema[selectedEntity].data || {}));
+ data[selectedEntity][selectedIndex].joins = {};
}
};
diff --git a/ext/afform/mock/tests/phpunit/api/v4/AfformPrefillUsageTest.php b/ext/afform/mock/tests/phpunit/api/v4/AfformPrefillUsageTest.php
new file mode 100644
index 000000000000..11e97469689b
--- /dev/null
+++ b/ext/afform/mock/tests/phpunit/api/v4/AfformPrefillUsageTest.php
@@ -0,0 +1,103 @@
+
+