I was working on a project recently where a developer wanted to have a drop down filter another drop down within a Model Driven App (MDA) form. There are some great ways to do this for lookups, but it took me a bit to see how it would work for drop downs. In the end we ended up creating a simple JavaScript that would map a parent choice with a child choice.
Table Setup
I have built a new table called Magic Choices with two choice columns in it, Parent column and Child column. The Parent column has the following choices, A,B,C,D The Child column has the following choices, Aa,Ab,Ac,Ba,Bb,Bc,Ca,Cb,Cc. The idea is that anytime the Parent column has A in it, the child choices Aa,Ab,Ac are shown. If B, Ba,Bb,Bc, are shown. If C, Ca,Cb,Cc are shown. If D, then all child choices are shown.



The Code
- The first part of the script will cache all child choices using the onload of the form
var originalChildOptions = []; function onLoadFilterSetup(executionContext) { var formContext = executionContext.getFormContext(); var childCtrl = formContext.getControl("andy_childchoice"); if (originalChildOptions.length === 0) { originalChildOptions = childCtrl.getOptions(); } filterChildOptions(executionContext); }
- The next part will execute on the on change of the parent choice drop down. It will get the parent choice and the child choice, if it finds the choices then it will continue.
function filterChildOptions(executionContext) { var formContext = executionContext.getFormContext(); var parentAttr = formContext.getAttribute("andy_parentchoice"); var childAttr = formContext.getAttribute("andy_childchoice"); var childCtrl = formContext.getControl("andy_childchoice"); if (!parentAttr || !childCtrl) return;
- Then we need to go out and clear the options of the child choice. There is no default way to do this, so we need to loop through all the choices.
for (var i = childCtrl.getOptions().length - 1; i >= 0; i--) { childCtrl.removeOption(childCtrl.getOptions()[i].value); }
- With the choices cleared we need to add the options back in once checked.
for (var i = 0; i < originalChildOptions.length; i++) { childCtrl.addOption(originalChildOptions[i]); }
- A matrix of all the parent choices to child choices is made
var visibleOptions = { 435700000: [435700000, 435700001, 435700002], 435700001: [435700003, 435700004, 435700005], 435700002: [435700006, 435700007, 435700008], };
- With the matrix loaded we remove anything not in the current allowed list. This keeps the dropdown clean and relevant.
if (allowed) { for (var i = 0; i < originalChildOptions.length; i++) { var option = originalChildOptions[i]; if (!allowed.includes(option.value)) { childCtrl.removeOption(option.value); } }
- The last part will allow for all choices to be shown if no parent is selected, or if an un mapped choice is selected.
if (!allowed.includes(childAttr.getValue())) { childAttr.setValue(null); } }
// Global variable to cache all child options
var originalChildOptions = [];
function onLoadFilterSetup(executionContext) {
var formContext = executionContext.getFormContext();
var childCtrl = formContext.getControl("andy_childchoice");
// Cache original child options (only once)
if (originalChildOptions.length === 0) {
originalChildOptions = childCtrl.getOptions();
}
filterChildOptions(executionContext); // Run filtering initially too
}
function filterChildOptions(executionContext) {
var formContext = executionContext.getFormContext();
var parentAttr = formContext.getAttribute("andy_parentchoice");
var childAttr = formContext.getAttribute("andy_childchoice");
var childCtrl = formContext.getControl("andy_childchoice");
if (!parentAttr || !childCtrl) return;
var parentValue = parentAttr.getValue();
// First, restore all original options
childCtrl.clearOptions(); // This still doesn't work, so instead we:
for (var i = childCtrl.getOptions().length - 1; i >= 0; i--) {
childCtrl.removeOption(childCtrl.getOptions()[i].value);
}
for (var i = 0; i < originalChildOptions.length; i++) {
childCtrl.addOption(originalChildOptions[i]);
}
// Define which values should be visible based on parent
var visibleOptions = {
435700000: [435700000, 435700001, 435700002], // A → Aa, Ab, Ac
435700001: [435700003, 435700004, 435700005], // B → Ba, Bb, Bc
435700002: [435700006, 435700007, 435700008], // C → Ca, Cb, Cc
};
var allowed = visibleOptions[parentValue];
if (allowed) {
for (var i = 0; i < originalChildOptions.length; i++) {
var option = originalChildOptions[i];
if (!allowed.includes(option.value)) {
childCtrl.removeOption(option.value);
}
}
// Optionally reset child value if current value isn't allowed
if (!allowed.includes(childAttr.getValue())) {
childAttr.setValue(null);
}
}
}
Form and Business Rules
With the code written we can add it to the form as a web resource. Then in the form set the On Load to onLoadFilterSetup.

Within the parent choice set the On Change to filterChildOptions

I have also added a business rule to the parent choice to hide the child choice if no selection is made

💡 This part could have also been done within the JavaScript, but I like to use business rules where I can
App
With it all put together, the Child choice is hidden if no parent is selected.

When a choice is made it is displayed and the choice is filtered or not depending on the parent selection.


💡 If the Parent choice is changed and the child is not within the parents group the child selection will be cleared.