Supporting Environment-specific Parameters in CRM 2011 Jscript

In this post I present an approach for managing environment-specific variables that you might use in your CRM 2011 jscript functions.  Imagine you have developed some jscript code that needs environment specific values such as the GUID of a specific view, workflow or dialog.  Here’s my approach using a custom entity and REST…

Here’s a scenario to provide some context.  In this post Rhett Clinton provides a jscript solution for defaulting a Customer Lookup to Contact rather than Account.

i.e. when you click on the Phone Call form’s “Sender” field the lookup that appears has “Look for” set to Contact rather than Account:

image

The line of code that does this is highlighted below:

image

The second line above is there to overcome a problem.  Without that 2nd line what you find is the lookup window switches to Contact but the default view in the lookup defers to whatever the default public view for the entity is – for Contact this is typically “My Active Contacts” – and this isn’t typically what we want.  

The second line of code changes the default view to “All Actives Contact” but a hardcoded value is being used.  Here’s how we can avoid this hardcoding…


Firstly, the best place for this configuration data is in a CRM entity record.  It’s an easy data store for us to query, it is fully securable and if required we can leverage CRM’s audit, field level security, and data import functionality.  Here’s my entity:

image

I have implemented a simple data model.  A record consists of an identifying name and then the configuration value.  The Value field is a multi-line text field which means it can support a simple single text field or numeric value or a more complicated XML value.

To query for this GUID from the Phone Call form I need to use a REST query.  Now, because REST uses asynchronous ajax calls we have to have be careful here and I need to split my jscript across 3 functions.  The first function is executed onload and switches the Customer lookup to default to the Contact entity.  It then calls function #2 which performs the REST query and queries for my environment-specific parameter value.  Function #3 contains the stuff that I want to happen once I have that parameter value, and it is called on success of the ajax query inside function 2.  I had to put this last bit of code into a seperate function and not just have it follow on in function 1 due to asynchronous timing issues.  Here are the 3 functions:

function OnLoad() {
    document.getElementById("from").setAttribute("defaulttype", "2");
    GetConfigValue("ActiveContactsViewGUID");
}

function GetConfigValue(ConfigParamName) {
    // Get the CRM URL 
    var serverUrl = Xrm.Page.context.getServerUrl();

    // Cater for URL differences between on premise and online 
    if (serverUrl.match(/\/$/)) {
        serverUrl = serverUrl.substring(0, serverUrl.length - 1);
    }

    // Specify the ODATA end point (this is the same for all CRM 2011 implementations) 
    var ODATA_ENDPOINT = "/XRMServices/2011/OrganizationData.svc";

    // Specify the ODATA entity collection (this needs to be specific to your entity) 
    var ODATA_EntityCollection = "/new_configurationSet";

    // Specify the ODATA filter 
    var ODATA_Query = "?$select=new_Value&$filter=new_name%20eq%20\'" + ConfigParamName + "\'&$top=1";

    // Build the URL 
    var ODATA_Final_url = serverUrl + ODATA_ENDPOINT + ODATA_EntityCollection + ODATA_Query;

    //Call the REST endpoint 
    $.ajax({
        type: "GET",
        contentType: "application/json; charset=utf-8",
        datatype: "json",
        url: ODATA_Final_url,
        beforeSend: function (XMLHttpRequest) {
            //Specifying this header ensures that the results will be returned as JSON.      
            XMLHttpRequest.setRequestHeader("Accept", "application/json");
        },
        success: function (data, textStatus, XmlHttpRequest) {
            //This function will trigger asynchronously if the Retrieve was successful 
            ChangeView(data.d.results[0].new_Value);
        },
        error: function (XmlHttpRequest, textStatus, errorThrown) {
            //This function will trigger asynchronously if the Retrieve returned an error 
            alert("ajax call failed");
        }
    });
}

function ChangeView(ConfigValue) {
    Xrm.Page.getControl("from").setDefaultView(ConfigValue);
}

Now because we are using REST we need to add references to web resources containing  json and jquery libraries (more details here):

image 


So, that’s the approach.  Now how do you apply this when you need it?  Simple.

  1. Whenever you have this need, Import this solution file to add the Configuration entity and jquery and json web resources to your system.
  2. Publish All Customisations.
  3. Create a Configuration record in the new Configuration entity, assign the environment variable a Name and then store the Value.   In each environment, this record needs to exist with the same Name, but obviously the Value will be environment specific.
  4. Add References to the jquery and json web resources on your CRM form.
  5. Add to your form script the GetConfigValue() function I have provided above.
  6. In your script call the GetConfigValue() function passing in the Name of the environment variable you wish to retrieve.  Warning: do not have any jscript below this point in this function.  Any subsequent jscript needs to go in  the function described below.
  7. Modify line 39 of the GetConfigValue() function and swap out “ChangeView” with the name of your function which contains the actions that need to execute once the environment parameter has been obtained. 
Advertisements

6 thoughts on “Supporting Environment-specific Parameters in CRM 2011 Jscript

  1. Pingback: Java Script in CRM 2011– Quick Reference Guide « Gareth Tucker's Microsoft CRM Blog

  2. Silvia Chaves

    I need your help, im try to conect to json with your code but a cant, this is my code:

    function obtenerCodigoArea()
    {
    var lookupItem = new Array();
    lookupItem = Xrm.Page.getAttribute(“cgs_pasid”).getValue();
    if (lookupItem[0] != null)
    {

    var name = lookupItem[0].name;
    var guid = lookupItem[0].id;
    var entType = lookupItem[0].entityType;

    var pagecontext = Xrm.Page.context;
    var serverUrl = pagecontext.getServerUrl();

    var ODATA_ENDPOINT = “/XRMServices/2011/OrganizationData.svc”;
    var ODATA_EntityCollection = “/cgs_pas_silviaSet”;
    var ODATA_Query = “?$select=cgs_cdigodearea&$filter=cgs_name eq ‘” + name + “‘”; // I NEED cgs_cdigodearea WHERE cgs_name =name

    var ODATA_Final_url = serverUrl + ODATA_ENDPOINT + ODATA_EntityCollection + ODATA_Query;

    $.ajax({

    type: “GET”,
    contentType: “application/json; charset=utf-8”,
    datatype: “json”,
    url: ODATA_Final_url,
    beforeSend: function (XMLHttpRequest) {
    XMLHttpRequest.setRequestHeader(“Accept”, “application/json”);
    },
    success: function (data, textStatus, XmlHttpRequest) {
    alert(data.d.results[0].cgs_pasid);
    },
    error: function (XmlHttpRequest, textStatus, errorThrown) {
    alert(“ajax call failed “+textStatus);
    }
    });

    }

    }

    The result es “ajax call failed error”
    errorThrown= bad Request

    Reply
  3. Gareth Tucker Post author

    I think I’ve spotted an issue with this approach. The GetConfigValue() function has an asynchronous ajax query in it so will potentially not pass a result back in time and any code back in the calling function will execute before the configuration value has been returned.

    I didn’t hit this issue when I wrote the above post but am hitting it now.

    I am afraid this means we can’t use GetConfigValue() as a reusable function.

    We can however use it as a template and for each instance where you have this requirement you can create a function like it. The trick is to have no trailing code in the function that calls the GetConfigValue() function. Instead, any trailing code needs to reside within the success area inside GetConfigValue(). This will ensure synchonous behavior.

    Reply
  4. Pingback: Building Custom Screens using Dialogs and Ribbon Buttons in CRM 2011 « Gareth Tucker's Microsoft CRM Blog

  5. Bart van der Vliet

    I think there are two possibilities to make GetConfigValue a reusable function. The first possibility is making the ajax call asynchronous, the second can be achieved to supply the callback function as a function parameter.

    I took the liberty to modify the script to make it so:

    1. Synchronous method:

    function GetConfigValue(key) {
    var result = null;

    var url = Xrm.Page.context.getServerUrl();
    var org = Xrm.Page.context.getOrgUniqueName(); // <– added this to make the script organization independent.

    if (url.match(/\/$/)) {
    url = url.substring(0, url.length – 1);
    org = ""; // online crm does not accept an organization in the url path.
    }

    var ODATA_ENDPOINT = "/XRMServices/2011/OrganizationData.svc";
    var ODATA_EntityCollection = "/new_configurationSet";
    var ODATA_Query = "?$select=new_value&$filter=new_name%20eq%20\'" + key + "\'&$top=1";
    var ODATA_Url = url + "/" + org + ODATA_ENDPOINT + ODATA_EntityCollection + ODATA_Query;

    $.ajax({
    type: "GET",
    contentType: "application/json; charset=utf-8",
    datatype: "json",
    async: false, // use this to make the ajax call asynchronous.
    url: ODATA_Url,
    beforeSend: function (XMLHttpRequest) {
    XMLHttpRequest.setRequestHeader("Accept", "application/json");
    },
    success: function (data, textStatus, XmlHttpRequest) {
    result = data.d.results[0].new_value;
    },
    error: function (XmlHttpRequest, textStatus, errorThrown) {
    throw textStatus;
    }
    });

    return result;
    }

    Usage:

    var setting = getConfigValue("TestKey");

    2. Asynchronous method (callback function is from the top of my head and untested):

    function getConfigValue(key, callback) {
    var result = null;

    var url = Xrm.Page.context.getServerUrl();
    var org = Xrm.Page.context.getOrgUniqueName(); // <– added this to make the script organization independent.

    if (url.match(/\/$/)) {
    url = url.substring(0, url.length – 1);
    org = ""; // online crm does not accept an organization in the url path.
    }

    var ODATA_ENDPOINT = "/XRMServices/2011/OrganizationData.svc";
    var ODATA_EntityCollection = "/new_configurationSet";
    var ODATA_Query = "?$select=new_value&$filter=new_name%20eq%20\'" + key + "\'&$top=1";
    var ODATA_Url = url + "/" + org + ODATA_ENDPOINT + ODATA_EntityCollection + ODATA_Query;

    $.ajax({
    type: "GET",
    contentType: "application/json; charset=utf-8",
    datatype: "json",
    url: ODATA_Url,
    beforeSend: function (XMLHttpRequest) {
    XMLHttpRequest.setRequestHeader("Accept", "application/json");
    },
    success: function (data, textStatus, XmlHttpRequest) {
    result = callback(data.d.results[0].new_value);
    },
    error: function (XmlHttpRequest, textStatus, errorThrown) {
    throw textStatus;
    }
    });

    return result;
    }

    Usage:

    function callbackFunction(setting) {
    alert(setting);
    }

    getConfigValue("TestKey", callbackFunction);

    Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s