So far in this series we've covered basic form events and field-level show/hide/required logic. This post covers the three field types that trip people up most: lookups, option sets, and subgrids — including how to filter a lookup so users only see relevant records.
Reading and Setting Lookup Values
A lookup's value is always an array of objects, even though it only ever holds one record on most forms. Each object has id, name, and entityType:
var formContext = executionContext.getFormContext();
var lookupValue = formContext.getAttribute("parentcustomerid").getValue();
if (lookupValue) {
var id = lookupValue[0].id;
var name = lookupValue[0].name;
var entityType = lookupValue[0].entityType;
}
// Setting a lookup value
formContext.getAttribute("parentcustomerid").setValue([
{
id: "00000000-0000-0000-0000-000000000000",
name: "Contoso Ltd",
entityType: "account"
}
]);
Filtering a Lookup's Results
By default a lookup searches the whole entity. To narrow it — say, only show contacts belonging to the account selected elsewhere on the form — use addPreSearch with a custom filter:
function onLoad(executionContext) {
var formContext = executionContext.getFormContext();
formContext.getControl("primarycontactid").addPreSearch(filterContactsByAccount);
}
function filterContactsByAccount(executionContext) {
var formContext = executionContext.getFormContext();
var accountValue = formContext.getAttribute("parentaccountid").getValue();
if (accountValue) {
var accountId = accountValue[0].id.replace("{", "").replace("}", "");
var filter = "<filter type='and'>" +
"<condition attribute='parentcustomerid' operator='eq' value='" + accountId + "' />" +
"</filter>";
formContext.getControl("primarycontactid").addCustomFilter(filter, "contact");
}
}
One gotcha: addCustomFilter only applies to the next search, so it has to run inside addPreSearch every time, not just once on load.
Working with Option Sets
Option sets (choice fields) store and return the underlying numeric value, not the label. Use getOption or getOptions when you need the label or want to build the list dynamically:
var attribute = formContext.getAttribute("new_prioritylevel");
// Read/write the numeric value
var currentValue = attribute.getValue(); // e.g. 2
attribute.setValue(1);
// Get the label for the current value
var options = attribute.getOptions();
var selected = options.find(function (o) { return o.value === currentValue; });
console.log(selected ? selected.text : "none selected");
// Remove an option a user shouldn't be able to pick
var control = formContext.getControl("new_prioritylevel");
control.removeOption(3);
Refreshing a Subgrid
Subgrids don't automatically know when related data changes elsewhere on the form. Refresh one explicitly after an action that affects its rows:
var gridControl = formContext.getControl("Subgrid_Orders");
if (gridControl) {
gridControl.refresh();
// Reading the currently loaded rows
var grid = gridControl.getGrid();
var rows = grid.getRows();
console.log("Row count: " + rows.getLength());
}
Common Pitfalls
- Treating a lookup value as a single object. It's always an array — forgetting the
[0]is one of the most common form-script bugs. - Comparing option set values as strings. They're numbers;
"2" === 2is false in JavaScript. - Custom filters that don't reset. If you conditionally apply a filter, make sure the "else" branch clears it, or users will get a stale filter after changing the related field.
- Calling grid methods before the subgrid has loaded. On OnLoad, wrap subgrid access in the control's
onLoadevent rather than assuming it's ready immediately.
What's Next
Next post: calling the Dataverse Web API directly from a form script — reading related records, creating child rows, and handling the async responses properly.
No comments:
Post a Comment