Dependent Option Sets in CRM 2011

04.04.11

It is a very common businesses requirement to limit the values in one Option Set (called picklist in previous versions of CRM) based on the value set in another one.  After investigating several ways of addressing this common business requirement, we have come up with code that does the following:

  • Provides robust filtering code that can be used globally (on any form).
  • Relationship setup must be easily done in one place.
  • Actual option set values and not the index must be used.

The sections below will walk you through a sample that filters the list of available countries based on the selected region.  For the purposes of this example, we will use the subset of values found in the following table:

Step 1: Create Options Sets

Create Region and Country option sets with all the possible values.

new_region

new_country

Step 2: Create the Web Resource

Create a new web resource, let’s call it DependentOptionset.

Copy the following code into the new web resource.  Note that the code is based primarily on the code found in the SDK sample with some minor modifications. 

function initDependentOptionSet()
{
    // Make sure that DependentOptionSetConfig was defined as expected
    if (typeof (DependentOptionSetConfig) == "undefined")
    {
        alert("DependentOptionSetConfig is not defined.");
        return;
    }
   
    //Fire the onchange event for the mapped optionset fields
    // so that the dependent fields are filtered for the current values.
    for (var depOptionSet in DependentOptionSetConfig)
    {
        var parent = DependentOptionSetConfig[depOptionSet].parent;
        Xrm.Page.data.entity.attributes.get(parent).fireOnChange();
    }
}

// This is the function set on the onchange event for
// parent fields
function filterDependentField(parentField, childField)
{
    // Make sure that DependentOptionSetConfig was defined as expected
    if (typeof (DependentOptionSetConfig) == "undefined")
    {
        alert("DependentOptionSetConfig is not defined.");
        return;
    }

    for (var depOptionSet in DependentOptionSetConfig)
    {
        var DependentOptionSet = DependentOptionSetConfig[depOptionSet];
       
        /* Match the parameters to the correct dependent optionset mapping*/
        if ((DependentOptionSet.parent == parentField) && (DependentOptionSet.dependent == childField))
        {
            /* Get references to the related fields*/
            var ParentField = Xrm.Page.data.entity.attributes.get(parentField);
            var ChildField = Xrm.Page.data.entity.attributes.get(childField);
          
            /* Capture the current value of the child field*/
            var CurrentChildFieldValue = ChildField.getValue();

            /* If the parent field is null the Child field can be set to null */
            if (ParentField.getValue() == null)
            {
                ChildField.setValue(null);
                ChildField.setSubmitMode("always");
                ChildField.fireOnChange();

                // Any attribute may have any number of controls
                // So disable each instance
                var controls = ChildField.controls.get()

                for (var ctrl in controls)
                {
                    controls[ctrl].setDisabled(true);
                }
               
                return;
            }

            for (var os in DependentOptionSet.options)
            {
                var Options = DependentOptionSet.options[os];
                var optionsToShow = Options.showOptions;
               
                /* Find the Options that corresponds to the value of the parent field. */
                if (ParentField.getValue() == Options.value)
                {
                    var controls = ChildField.controls.get();

                    /*Enable the field and set the options*/
                    for (var ctrl in controls)
                    {
                        controls[ctrl].setDisabled(false);
                        controls[ctrl].clearOptions();

                        var existingOptions = controls[ctrl].getAttribute().getOptions();

                        for (var option in optionsToShow)
                        {
                            for (eo = 0; eo < existingOptions.length; eo++)
                            {
                                if (existingOptions[eo].value == optionsToShow[option])
                                {
                                    controls[ctrl].addOption(existingOptions[eo]);
                                }
                            }
                        }

                    }
                   
                    /*Check whether the current value is valid*/
                    var bCurrentValueIsValid = false;
                    var ChildFieldOptions = optionsToShow;

                    for (var validOptionIndex in ChildFieldOptions)
                    {
                        var OptionDataValue = ChildFieldOptions[validOptionIndex];

                        if (CurrentChildFieldValue == OptionDataValue)
                        {
                            bCurrentValueIsValid = true;
                            break;
                        }
                    }
                   
                    /* If the value is valid, set it. If not, set the child field to null */
                    if (bCurrentValueIsValid)
                    {
                        ChildField.setValue(CurrentChildFieldValue);
                    }
                    else
                    {
                        ChildField.setValue(null);
                    }
                   
                    ChildField.setSubmitMode("always");
                    ChildField.fireOnChange();
                    break;
                }
                /* No values were specified for this parent field value */
                else
                {
                    var controls = ChildField.controls.get();

                    /*Enable the field and set the options*/
                    for (var ctrl in controls)
                    {
                        controls[ctrl].setDisabled(true);
                        controls[ctrl].clearOptions();

                        ChildField.setValue(null);
                        ChildField.setSubmitMode("always");
                        ChildField.fireOnChange();
                        break;
                    }
                }
            }
        }
    }
}

Save and publish.  Now the code is globally available. Next we need to add it to the form.

Step 3: Add the code to the form

There are two functions required. The first, initDependentOptionSet(), needs to be added to the form’s OnLoad event. This method fires the onchange event for each of the parent fields in order to ensure that the child fields are properly filtered when the form opens. The second, filterDependentField(parentField, childField), needs to be added to each of the parent field’s OnChange events. This function does all the heavy lifting.

Add the Web Resource we created above to the Form Libraries:

Add the form’s OnLoad Event Handler:

Add the parent field’s OnChange Event Handler (Don’t forget to pass in the parent and child field names):

Finally, we need to define the relationship between the two fields by defining DependentOptionSetConfig.

Step 4: Define DependentOptionSetConfig

The DependentOptionSetConfig  variable is required by the global code and needs to be defined in the OnLoad event of the form it will be used on. Make sure that it is not defined within any functions. It contains the parent/child relationships we want.

Below is an example of what it would look like for our region/country example above: 

var DependentOptionSetConfig = 
    [{"parent": "new_region", "dependent": "new_country",
        "options": [{ "value": "100000000", "label": "APAC", "showOptions": ["100000002", "100000005", "100000006", "100000008"] },
                     {"value": "100000001", "label": "Europe", "showOptions": ["100000000", "100000003", "100000004", "100000007", "100000009"]},
                     {"value": "100000002", "label": "North America", "showOptions": ["100000001", "100000010"]}
                    ]}
    ];

« Back to index