Sometimes, when creating a form in Model-Driven Apps (MDA), we want to autofill fields based on the relationship in a lookup. This can be triggered either when the lookup is updated, when the form is loaded, or both. In this post, I will explain how to use a generic script to achieve this and how to configure it in the form.
Let’s start with a simple example:
I have a table for Countries, a table for Car Brands, and a table for Car Models. In the Car Model table, there is a lookup field pointing to the Car Brands table. What I want to achieve is this: when the user selects a brand in the lookup field, the following fields are automatically populated:
The Country (a lookup field).
The Country Full Name (a text field linked to the Country lookup).
The Car Brand Full Name (a text field).
So, let’s start by uploading the script into our solution:
/**
* Updates lookup or normal fields dynamically based on arrays of configuration parameters.
*
* @license CC0-1.0
*
* Created by ChatGPT with the invaluable help and input of Arcadi Burria.
*
* @param {object} executionContext - The execution context from the form event.
* @param {array} _sourceOriginLKLogicNames - Array of logical names of source lookup fields on the form.
* @param {array} _sourceToUpdateLogicNames - Array of logical names of fields (lookup or normal) to update on the form.
* @param {array} _destinationEntityLogicalNames - Array of logical names of entities being queried.
* @param {array} _lookupFieldNames - Array of field names (schema names) to expand for lookups. Leave empty for normal fields.
* @param {array} _normalFieldNames - Array of logical names of fields to retrieve directly for normal fields. Leave empty for lookups.
* @param {array} _referencedEntityLogicNames - Array of logical names of referenced entity types for lookup fields.
* Leave empty for normal fields.
* @param {array} _referencedEntityIdFieldLogicNames - Array of logical names of referenced entity ID fields for lookup fields.
* Leave empty for normal fields.
* @param {array} _referencedEntityPrimaryColumnLogicNames - Array of logical names of referenced entity primary column fields
* for lookup fields. Leave empty for normal fields.
*/
function setFieldsFromSource(
executionContext,
_sourceOriginLKLogicNames,
_sourceToUpdateLogicNames,
_destinationEntityLogicalNames,
_lookupFieldNames,
_normalFieldNames,
_referencedEntityLogicNames,
_referencedEntityIdFieldLogicNames,
_referencedEntityFieldPrimaryColumnLogicNames
) {
console.log("setFieldsFromSource - v2.2");
const formContext = executionContext.getFormContext();
// Ensure all arrays are of the same length
if (
_sourceOriginLKLogicNames.length !== _sourceToUpdateLogicNames.length ||
_sourceToUpdateLogicNames.length !== _destinationEntityLogicalNames.length ||
_destinationEntityLogicalNames.length !== _lookupFieldNames.length ||
_lookupFieldNames.length !== _normalFieldNames.length ||
_normalFieldNames.length !== _referencedEntityLogicNames.length ||
_referencedEntityLogicNames.length !== _referencedEntityIdFieldLogicNames.length ||
_referencedEntityIdFieldLogicNames.length !== _referencedEntityFieldPrimaryColumnLogicNames.length
) {
console.error("All parameter arrays must have the same length.");
return;
}
// Process each field configuration
for (let i = 0; i < _sourceOriginLKLogicNames.length; i++) {
const sourceOriginLKLogicName = _sourceOriginLKLogicNames[i];
const sourceToUpdateLogicName = _sourceToUpdateLogicNames[i];
const destinationEntityLogicalName = _destinationEntityLogicalNames[i];
const lookupFieldName = _lookupFieldNames[i];
const normalFieldName = _normalFieldNames[i];
const referencedEntityLogicName = _referencedEntityLogicNames[i];
const referencedEntityIdFieldLogicName = _referencedEntityIdFieldLogicNames[i];
const referencedEntityPrimaryColumnLogicName = _referencedEntityFieldPrimaryColumnLogicNames[i];
// Get the source lookup value
const originLookup = formContext.getAttribute(sourceOriginLKLogicName).getValue();
// Check if the source lookup has a value
if (originLookup && originLookup.length > 0) {
// Get the source record ID
const originId = originLookup[0].id.replace("{", "").replace("}", "");
// Build the query string for $expand (lookups) or $select (normal fields)
const queryString = lookupFieldName
? `?$expand=${lookupFieldName}` // For lookups, use $expand
: `?$select=${normalFieldName}`; // For normal fields, use $select
// Retrieve the destination field or lookup from the record
Xrm.WebApi.retrieveRecord(
destinationEntityLogicalName,
originId,
queryString
).then(
(result) => {
if (lookupFieldName) {
// If lookup, access the expanded data
const expandedData = result[lookupFieldName];
if (expandedData) {
formContext.getAttribute(sourceToUpdateLogicName).setValue([{
id: expandedData[referencedEntityIdFieldLogicName],
name: expandedData[referencedEntityPrimaryColumnLogicName],
entityType: referencedEntityLogicName
}]);
// Trigger the OnChange event for the lookup field
formContext.getAttribute(sourceToUpdateLogicName).fireOnChange();
} else {
// Clear the target field if no value is found
formContext.getAttribute(sourceToUpdateLogicName).setValue(null);
formContext.getAttribute(sourceToUpdateLogicName).fireOnChange();
}
} else if (normalFieldName) {
// If normal field, retrieve and set its value
const fieldValue = result[normalFieldName];
const field = formContext.getAttribute(sourceToUpdateLogicName);
if (field) {
field.setValue(fieldValue || null); // Set the value or clear if null
} else {
console.warn(`Field ${sourceToUpdateLogicName} does not exist on the form.`);
}
}
},
(error) => {
console.error(`Error retrieving field from '${destinationEntityLogicalName}':`, error.message);
}
);
} else {
// Clear the target field if the source lookup is empty
const targetField = formContext.getAttribute(sourceToUpdateLogicName);
if (targetField) {
targetField.setValue(null);
targetField.fireOnChange();
}
}
}
}
Let’s create a new web resource:
Now, we’ll save and publish the web resource:
Next, we can go to the form and configure it. In this case, we’ll set it to trigger when a lookup value changes:
For the library, we may need to locate it using the “+ Add Library” option. The function will be the name of the function in the script, setFieldsFromSource
. Make sure to check ‘Pass execution context’, as it is the first parameter.
Each parameter is an array of strings, allowing us to update multiple fields simultaneously. In this example, I will pass two parameters because I want to update two fields: the Country and the Car Brand Full Name.
_sourceOriginLKLogicNames: This is the logical name of the lookup field in the current entity, which links to the fields we want to retrieve. In our case, for both fields, it will be the Car Brand, and we can find it in the table columns.
So this first parameter in my case will be: ["arc7879_brandid", "arc7879_brandid"]
_sourceToUpdateLogicNames: These are the logical names of the fields we want to update in the form. The first one is the Country lookup, and the second is the text field for the Car Brand Full Name.
["arc7879_countryid", "arc7879_carbrandfullname"]
_destinationEntityLogicalNames: This is the logical name of the entity that the lookup (in this case, the Car Brand) is pointing to:
["arc7879_carbrand", "arc7879_carbrand"]
_lookupFieldNames: This is the schema name (the one with PascalCase) of the lookup we are trying to copy. If we are copying a normal field, we pass a blank parameter.
["arc7879_CountryISOCode", ""]
_normalFieldNames: These are the logical names of the fields we want to copy. If the field is a lookup, we leave it blank:
["", "arc7879_fullname"]
The final three fields will only have a value if we are trying to copy a lookup. Otherwise, we’ll leave them blank. Since it’s a lookup, it will point to a third entity, and the information we need is located in that entity— in this case, the Countries entity in our example.
_referencedEntityLogicNames: Logic name of the entity:
["arc7879_country", ""]
_referencedEntityIdFieldLogicNames: Logic name of the ID column:
["arc7879_countryid", ""]
_referencedEntityPrimaryColumnLogicNames: Logic name of the Primary column:
["arc7879_countrycode",""]
So, in my example, the parameters are:
["arc7879_brandid", "arc7879_brandid"],
["arc7879_countryid", "arc7879_carbrandfullname"],
["arc7879_carbrand", "arc7879_carbrand"],
["arc7879_CountryISOCode", ""],
["", "arc7879_fullname"],
["arc7879_country", ""],
["arc7879_countryid", ""],
["arc7879_countrycode",""]
The script triggers the event handlers, so I can also configure the same script for the Country lookup. This way, it will be triggered both when the Country is manually changed and when the Brand is changed (which also updates the Country).
["arc7879_countryid"],
["arc7879_countryfullname"],
["arc7879_country"],
[""],
["arc7879_name"],
[""],
[""],
[""]
We save an publish the form:
And let’s try it out:
If it doesn’t work, try refreshing the page with Ctrl + F5 and make sure the script is working by checking the developer console—there should be a log message: “setFieldsFromSource – v2.2”.
Leave a Reply