September 5, 2016

Dependent OptionSet Generator - Overview - Part 1

Some days ago I released a new Dynamics CRM tool called Dependent OptionSet Generator.
As the name suggests it helps to create and update dependent OptionSets, one of the possible approaches to filter data inside CRM UI.

Scott Durow wrote an overview about this argument on his blog: "Option-Set, Lookup or Autocomplete", it's very useful if you plan to use dependent OptionSets in order to know the pro and cons of this approach.

I want to thank Scott not only for his post but because he found the time to meet me when I was in London a couple of months ago (photo). He was the first person I talked about this little project and we had a pleasant discussion not only about Dynamics CRM, thanks again Scott!
During my short visit in London I also met again Ramón Tébar Bueno and it's really nice to spend some time with other members of the Dynamics CRM Community, plus he took me to a typical British Pub!

Now back to the main reason of this post, the Dependent OptionSet Generator.
Starting from CRM 2011 the possibility to filter OptionSets entries was available thanks to the Xrm supported methods. Microsoft also provided a solution inside their CRM SDK to implement the dependent OptionSets but the most tedious process, the generation of the XML file required to map the entries, was delegated completely to the user.

Creating manually the XML document has always been a bummer, especially when you need to deal with several entries, so developers and consultants suggest to use filtered Lookups instead.
However there are implementations where OptionSets are preferred due to the Multi-Language support, to facilitate CRM users (at eCraft, the company I work for, we often have customers with Finnish and Swedish users) or because it's required by the law (for example in some provinces of Canada).

Microsoft kept updated its Dependent OptionSet solution during the years and with the latest CRM 2016 version they changed the configuration format structure from XML to JSON, probably in order to simplify the creation of the file.

My solution consists of two parts:
  • The first part is the JavaScript Library to be included inside the CRM forms in order to filter the OptionSets
  • The second part is a Web Application to build and maintain the JSON configuration required by the JavaScript Library
This post will cover the first part, I will write another post (or more) regarding the Web Application.

The JavaScript Library included in my solution is based on the CRM 2016 SDK version called sample_SDK.DependentOptionSetSample.js, you can find the source code here. Except for the first point (Different Namespace) all the other changes were done in order to make the script compatible with CRM 2011 supported browsers.

  • Different Namespace: because I didn't change the name of the functions I preferred to use a different namespace (DO instead of SDK) in order to avoid issues if the SDK library and my library are loaded in the same form.

  • JSON: The CRM 2016 library uses JSON, but the original Microsoft Library created for CRM 2011 uses XML. When the CRM 2011 library was created Dynamics CRM supported only Internet Explorer, including old versions like IE7. These old IE versions don't have the JSON object, so (probably) they decided to parse an XML object instead. My library in order to be compatible with these IE versions includes the JSON2 library. I can include it without issues because the first line of the library checks if JSON is already defined
    if("object"!==typeof JSON)JSON={};
    
    in this way old browsers can support JSON methods like JSON.parse.

  • getClientUrl: This Xrm method was introduced with CRM 2011 UR12, but my library in order to be compatible with pre-UR12 CRM 2011 instances checks if getClientUrl is available before using it. I used this code snippet in many of my samples in order to avoid creating a JavaScript only to handle CRM 2011 versions:
    var serverUrl;
    if (Xrm.Page.context.getClientUrl !== undefined) {
        serverUrl = Xrm.Page.context.getClientUrl();
    } else {
        serverUrl = Xrm.Page.context.getServerUrl();
        if (serverUrl.match(/\/$/)) { serverUrl = serverUrl.substring(0, serverUrl.length - 1); }
    }
    
  • response and responseText: Older versions of Internet Explorer don't implement the response property inside the object returned by the XmlHttpRequest, so if the property is not defined we will use the responseText instead.
    if (this.response !== undefined) { wrContent = this.response; }
    // handling responseText for older IE versions
    else { if (this.responseText !== undefined) { wrContent = this.responseText; } }
    
  • setTimeout: The setTimeout syntax used inside the SDK sample is not compatible with older IE versions, an easy fix is to wrap the function called inside an anonymous function:
    // original SDK sample code
    setTimeout(SDK.DependentOptionSet.filterOptions, 100, parentFieldParam, childFieldParam, dependentOptionSet);    
    // setTimeout rewritten for older IE versions
    setTimeout(function() { DO.DependentOptionSet.filterOptions(parentFieldParam, childFieldParam, dependentOptionSet); }, 100);
    
  • getClient and CRM for Tables and Phones: The SDK sample handles also the compatibility with CRM for mobile devices, but in order to handle the call to the method Xrm.Page.context.client.getClient() it's necessary to check if the context object contains the client property and its getClient method:
    if (Xrm.Page.context.client !== undefined && Xrm.Page.context.client.getClient !== undefined) {
        // ... rest of the code
    }
    
  • forEach: The SDK sample uses forEach to iterate the available values for the child OptionSet, but forEach is not available for older IE versions, the code has been rewritten to use a standard for cycle:
    //original SDK sample code
    validOptionValues.forEach(function (optionValue) { /* ... */ })
    // standard for cycle
    for (var count = 0; count < validOptionValues.length; count++) { /* ... */ }
    
Making the library compatible with CRM 2011 was more an exercise for me, CRM 2011 is officially not supported and I hope that many instances have been already migrated to newer versions, but I am aware that this is not always possible.

However the main part of my solution is the Web Application to create/update the JSON configuration, if you are already using the CRM 2016 SDK Sample in your CRM, you can use the Web Application without using my library and just update the Web Resources.

September 3, 2016

Passing Arrays to JavaScript Web Resources as parameter

Despite the availability of Business Rules, JavaScript is still an important way to customize Dynamics CRM UI, especially in complex scenarios (or when you just need to hide a section).

If you ever need to pass an Array as parameter inside the Event Handler form, you can declare it with the square brackets or using the Array keyword:
// square brackets
[1, 2, 3, 4 ,5]
// Array keyword
new Array(1, 2, 3, 4, 5)
CRM Event Handler:


As example we write a function to hide fields passing their logicalname as an array:
["name", "accountnumber", "telephone1"]
the function handles the parameter in this way:
function HideFields(fields) {
    for (var count = 0; count < fields.length; count++) {
        if (Xrm.Page.getControl(fields[count]) != null) {
            Xrm.Page.getControl(fields[count]).setVisible(false);
        }
    }
}
If a parameter is an Array, multiple parameters can still be passed inside the Event Handler. For example:
// note the comma before the array is declared
true, ["name", "accountnumber", "telephone1"]
In this way we are sending a boolean as first parameter to our updated function:
function ShowHideFields(isVisible, fields) {
    for (var count = 0; count < fields.length; count++) {
        if (Xrm.Page.getControl(fields[count]) != null) {
            Xrm.Page.getControl(fields[count]).setVisible(isVisible);
        }
    }
}
There is another way to pass multiple parameters without declaring each parameter inside the function definition and without using an Array: the arguments variable.
In JavaScript the arguments variable holds automatically all the parameters passed to a function.
We can rewrite the previous example, inside the Event Handler we pass the values as separate parameters, not as an array:
// no square brackets, no Array Keywords, just 3 separate parameters
"name", "accountnumber", "telephone1"
and our function is simply this one:
function HideFields() {
    for (var count = 0; count < arguments.length; count++) {
        if (Xrm.Page.getControl(arguments[count]) != null) {
            Xrm.Page.getControl(arguments[count]).setVisible(false);
        }
    }
}
But if we want to handle a specific element (like the first element) we need to take care inside the code:
// we decided that the first parameter will contain the value for the isVisible variable
true, "name", "accountnumber", "telephone1"
function ShowHideFields() {
    // this function requires at least 2 parameters
    if (arguments.length > 1) {
        var isVisible = arguments[0];
        // we start the for cycle from 1 instead of 0
        for (var count = 1; count < arguments.length; count++) {
            if (Xrm.Page.getControl(arguments[count]) != null) {
                Xrm.Page.getControl(arguments[count]).setVisible(isVisible);
            }
        }
    }
}