CRM Dependent Option-sets

Applies to: CRM 2015 on premise and CRM 2015 Online.

A common requirement is for the values in one option set to be filtered by the choice of a value in another option set. But having the ability to break this down into 3 optionsets can be a valuable feature. This blog describes one approach using a JScript library, form events and an XML web resource.

  • The idea here is to provide a generic, reusable JScript library that can used for any pair of option set fields.
  • It allows for a chain of dependent option set fields. Because the options of each dependent option set field are filtered based on the value of another field, additional option set field options can be filtered by the option chosen in the first dependent option set field. This allows for the possibility of a set of hierarchically dependent option set fields.
  • The filtering of dependent options is set in an XML web resource. This allows for changing the option mappings without changing the code. Editing an XML web resource is easier for a non-developer to configure options with less opportunity to break the code.
  • As the filtering is based on the data value of the options rather than the text in the options it support multiple languages.

The example / table below shows the desired filtering of option set options. We have added three option sets to an ‘Account’ (company) form to show what type of business this company is. We have three identifiers ‘Type’ where there is a choice of either Private or Public, the second category to identify the company is what ‘Sector’ the company is in and finally the third sub category adds further details.

 

Type
(sample_type)

 

Category
(sample_sector)

Sub Category
(sample_subsector)

Value:727000000 Label: Private Value:727000000 Label: Commercial Value:727000000 Label: Hotel and Accommodation
Value:727000001 Label: Retail
Value:727000002 Label: Factory/Manufacturing
Value:727000003 Label: Science/Technology
Value:727000001 Label: Housing Value:727000004 Label: Residential
Value:727000005 Label: Student Accommodation
Value:727000006 Label: Extra Care
Value:727000002 Label: Arts and Leisure Value:727000007 Label: Leisure Facility
Value:727000008 Label: Arts
Value:727000009 Label: Sports Pitch
Value:727000001 Label: Public Value:727000003 Label: Government Value:727000011 Label: Justice
Value:727000012 Label: Defence
Value:727000013 Label: Environment
Value:727000014 Label: Transport
Value:727000004 Label: Heritage Value:727000015 Label: World heritage sites
Value:727000016 Label: Listed buildings
Value:727000017 Label: Conservation areas
Value:727000018 Label: Registered parks and gardens
Value:727000005 Label: Local Authority Value:727000019 Label: County
Value:727000020 Label: Borough
Value:727000021 Label: Parish Council
Value:727000022 Label: District
Value:727000006 Label: Health Value:727000023 Label:NHS Trust
Value:727000024 Label: NHS Foundation Trusts
Value:727000025 Label: GP Surgery
Value:727000026 Label: NHS Property Services
Value:727000007 Label: Infrastructure Value:727000027 Label: Bus
Value:727000028 Label: Rail
Value:727000029 Label: Road

 

 

 Enable the filtering

 

1.    Convert the desired filtering of options into the following XML document and upload it as an XML web resource titled sample_DependentOptionSetConfig.xml. The label values are included to make the document easier to edit but are not used in the script that filters the options.

XML

    <DependentOptionSetConfig entity=”account” >

<ParentField id=”sample_type”

label=”Type”>

<DependentField id=”sample_subcategory”

label=”Category” />

<Option value=”727000000″

label=”Private”>

<ShowOption value=”727000000″

label=”Commercial” />

<ShowOption value=”727000001″

label=”Housing” />

<ShowOption value=”727000002″

label=”Arts and Leisure” />

</Option>

<Option value=”727000001″

label=”Public”>

<ShowOption value=”727000003″

label=”Government” />

<ShowOption value=”727000004″

label=”Heritage” />

<ShowOption value=”727000005″

label=”Local Authority” />

<ShowOption value=”727000006″

label=”Health” />

<ShowOption value=”727000007″

label=”Infrastructure” />

</Option>

</ParentField>

<ParentField id=”sample_sector”

label=”Category”>

<DependentField id=”sample_subsector”

label=”Sub Category” />

<Option value=”727000000″

label=”Commercial”>

<ShowOption value=”727000000″

label=”Hotel and Accommodation” />

<ShowOption value=”727000001″

label=”Retail” />

<ShowOption value=”727000002″

label=”Factory/Manufacturing” />

<ShowOption value=”727000003″

label=”Scrience/Technology” />

</Option>

<Option value=”727000001″

label=”Housing”>

<ShowOption value=”727000004″

label=”Residential” />

<ShowOption value=”727000005″

label=”Student Accommodation” />

<ShowOption value=”727000006″

label=”Extra Care” />

</Option>

<Option value=”727000002″

label=”Arts and Leisure”>

<ShowOption value=”727000007″

label=”Leisure Facility” />

<ShowOption value=”727000008″

label=”Arts” />

<ShowOption value=”727000009″

label=”Sports Pitch” />

</Option>

<Option value=”727000003″

label=”Government”>

<ShowOption value=”727000011″

label=”Justice” />

<ShowOption value=”727000012″

label=”Defence” />

<ShowOption value=”727000013″

label=”Environment” />

<ShowOption value=”727000014″

label=”Transport” />

</Option>

<Option value=”727000004″

label=”Heritage”>

<ShowOption value=”727000015″

label=”World heritage sites” />

<ShowOption value=”727000016″

label=”Listed buildings” />

<ShowOption value=”727000017″

label=”Conservation areas” />

<ShowOption value=”727000018″

label=”Registered parks and gardens” />

</Option>

<Option value=”727000005″

label=”Local Authority”>

<ShowOption value=”727000019″

label=”County” />

<ShowOption value=”727000020″

label=”Borough” />

<ShowOption value=”727000021″

label=”Parish” />

<ShowOption value=”727000022″

label=”District” />

</Option>

<Option value=”727000006″

label=”Health”>

<ShowOption value=”727000023″

label=”NHS Trust” />

<ShowOption value=”727000024″

label=”NHS Foundation Trusts” />

<ShowOption value=”727000025″

label=”GP Surgery” />

<ShowOption value=”727000026″

label=”NHS Property Services” />

</Option>

<Option value=”727000007″

label=”Infrastructure”>

<ShowOption value=”727000027″

label=”Bus” />

<ShowOption value=”727000028″

label=”Rail” />

<ShowOption value=”727000029″

label=”Road” />

</Option>

</ParentField>

</DependentOptionSetConfig>

 

2.    Create a JScript web resource named sample_SDK.DependentOptionSetSample.js using the following code.

JScript

//If the SDK namespace object is not defined, create it.

if (typeof (SDK) == “undefined”)

{ SDK = {}; }

// Create Namespace container for functions in this library;

SDK.DependentOptionSet = {};

SDK.DependentOptionSet.init = function (webResourceName) {

//Retrieve the XML Web Resource specified by the parameter passed

var clientURL = Xrm.Page.context.getClientUrl();

 

var pathToWR = clientURL + “/WebResources/” + webResourceName;

var xhr = new XMLHttpRequest();

xhr.open(“GET”, pathToWR, true);

xhr.setRequestHeader(“Content-Type”, “text/xml”);

xhr.onreadystatechange = function () { SDK.DependentOptionSet.completeInitialization(xhr); };

xhr.send();

};

SDK.DependentOptionSet.completeInitialization = function (xhr) {

if (xhr.readyState == 4 /* complete */) {

if (xhr.status == 200) {

xhr.onreadystatechange = null; //avoids memory leaks

var JSConfig = [];

var ParentFields = xhr.responseXML.documentElement.getElementsByTagName(“ParentField”);

for (var i = 0; i < ParentFields.length; i++) {

var ParentField = ParentFields[i];

var mapping = {};

mapping.parent = ParentField.getAttribute(“id”);

mapping.dependent = SDK.Util.selectSingleNode(ParentField, “DependentField”).getAttribute(“id”);

mapping.options = [];

var options = SDK.Util.selectNodes(ParentField, “Option”);

for (var a = 0; a < options.length; a++) {

var option = {};

option.value = options[a].getAttribute(“value”);

option.showOptions = [];

var optionsToShow = SDK.Util.selectNodes(options[a], “ShowOption”);

for (var b = 0; b < optionsToShow.length; b++) {

var optionToShow = {};

optionToShow.value = optionsToShow[b].getAttribute(“value”);

optionToShow.text = optionsToShow[b].getAttribute(“label”);

option.showOptions.push(optionToShow);

}

mapping.options.push(option);

}

JSConfig.push(mapping);

}

//Attach the configuration object to DependentOptionSet

//so it will be available for the OnChange events

SDK.DependentOptionSet.config = JSConfig;

//Fire the onchange event for the mapped optionset fields

// so that the dependent fields are filtered for the current values.

for (var depOptionSet in SDK.DependentOptionSet.config) {

var parent = SDK.DependentOptionSet.config[depOptionSet].parent;

Xrm.Page.data.entity.attributes.get(parent).fireOnChange();

}

}

}

};

// This is the function set on the onchange event for

// parent fields

SDK.DependentOptionSet.filterDependentField = function (parentField, childField) {

for (var depOptionSet in SDK.DependentOptionSet.config) {

var DependentOptionSet = SDK.DependentOptionSet.config[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();

 

for (var option in optionsToShow) {

controls[ctrl].addOption(optionsToShow[option]);

}

 

}

/*Check whether the current value is valid*/

var bCurrentValueIsValid = false;

var ChildFieldOptions = optionsToShow;

 

for (var validOptionIndex in ChildFieldOptions) {

var OptionDataValue = ChildFieldOptions[validOptionIndex].value;

 

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;

}

}

}

}

};

 

SDK.Util = {};

//Helper methods to merge differences between browsers for this sample

SDK.Util.selectSingleNode = function (node, elementName) {

if (typeof (node.selectSingleNode) != “undefined”) {

return node.selectSingleNode(elementName);

}

else {

return node.getElementsByTagName(elementName)[0];

}

};

SDK.Util.selectNodes = function (node, elementName) {

if (typeof (node.selectNodes) != “undefined”) {

return node.selectNodes(elementName);

}

else {

return node.getElementsByTagName(elementName);

}

};

 

3.    Add the sample_SDK.DependentOptionSetSample.js script web resource to the JScript libraries available for the account form.

4.    In the Onload event for the account form, configure the event handler to call the SDK.DependentOptionSet.init function and pass in the name of the XML web resource as a parameter. Use the field on the Handler Properties dialog box to enter: “sample_DependentOptionSetConfig.xml” into the field comma separated list of parameters that will be passed to the function.

5.    In the OnChange event for the Type field, set the Function to SDK.DependentOptionSet.filterDependentField.

 

In the comma separated list of parameters that will be passed to the function text box enter: “sample_type”, “sample_sector”.

 

6.     In the OnChange event for the Category field, set the Function to SDK.DependentOptionSet.filterDependentField.

In the comma separated list of parameters that will be passed to the function text box enter: “sample_sector”, “sample_subsector”.

 

7.   Save and publish all customizations.