CRM Software Logo

Microsoft Dynamics 365 & Dynamics CRM experts provide reviews and opinions to aid professionals with the Dynamics 365 selection process.

 
 
Hitachi Solutions America Bloggers

Using the FetchXML CRM 2011 Service within a JavaScript Web Resource

Email | Print

Last time we looked at using the FetchUtil.js file inside of an HTML file. This time, I’m going to show you how to execute fetch from JavaScript. I’m also going to talk a little about organizing your web resources and talk briefly about when to use the fetch. Before we begin, let’s lay some of the ground work here.

Keeping it Organized

CRM 2011 has some awesome features and web resources is certainly one of them. It’s important when you create your web resources to have some organization. The more you extend CRM, the easier it would be to start messing others up. I think the best route is to use your abbreviation, followed by the the folder, followed by the file. That sounds a little confusing, so lets look at an example.

Today we are going to create two files. The first is our JavaScript Fetch utility. The second is a JavaScript file specific to our incident entity. In fact, the incident entity is all part of our incidentX project. On the other hand, the Fetch Utility is going to be used across multiple projects. Now there isn’t any need to upload the Fetch Utility inside each project that needs it. So we will create two web resources like so:

  • new_utils/FetchUtil.js
  • new_incidentX/showMainPhone.js

Now, if I want to add some custom icons and other things to my incidentX project, I can simply do so in the new_incidentX folder.

Adding the Web Resources

Let’s look at both of the files and how they will appear on the form.

FetchUtil.js

var XMLHTTPSUCCESS = 200;
var XMLHTTPREADY = 4;

function FetchUtil(sOrg, sServer) {
this.org = sOrg;
this.server = sServer;

if (sOrg == null) {
if (typeof (ORG_UNIQUE_NAME) != "undefined") {
this.org = ORG_UNIQUE_NAME;
}
}

if (sServer == null) {
this.server = window.location.protocol + "//" + window.location.host;
}
}

FetchUtil.prototype._ExecuteRequest = function (sXml, sMessage, fInternalCallback, fUserCallback) {
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("POST", this.server + "/XRMServices/2011/Organization.svc/web", (fUserCallback != null));
xmlhttp.setRequestHeader("Accept", "application/xml, text/xml, */*");
xmlhttp.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
xmlhttp.setRequestHeader("SOAPAction", "
http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute");

if (fUserCallback != null) {
//asynchronous: register callback function, then send the request.
var crmServiceObject = this;
xmlhttp.onreadystatechange = function () {
fInternalCallback.call(crmServiceObject, xmlhttp, fUserCallback)
};
xmlhttp.send(sXml);
} else {
//synchronous: send request, then call the callback function directly
xmlhttp.send(sXml);
return fInternalCallback.call(this, xmlhttp, null);
}
}

FetchUtil.prototype._HandleErrors = function (xmlhttp) {
/// <summary>(private) Handles xmlhttp errors</summary>
if (xmlhttp.status != XMLHTTPSUCCESS) {
var sError = "Error: " + xmlhttp.responseText + " " + xmlhttp.statusText;
alert(sError);
return true;
} else {
return false;
}
}

FetchUtil.prototype.Fetch = function (sFetchXml, fCallback) {
/// <summary>Execute a FetchXml request. (result is the response XML)</summary>
/// <param name="sFetchXml">fetchxml string</param>
/// <param name="fCallback" optional="true" type="function">(Optional) Async callback function if specified. If left null, function is synchronous </param>

var request = "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\">";
request += "<s:Body>";

request += '<Execute xmlns="http://schemas.microsoft.com/xrm/2011/Contracts/Services">' + '<request i:type="b:RetrieveMultipleRequest" ' + ' xmlns:b="http://schemas.microsoft.com/xrm/2011/Contracts" ' + ' xmlns:i="http://www.w3.org/2001/XMLSchema-instance">' + '<b:Parameters xmlns:c="http://schemas.datacontract.org/2004/07/System.Collections.Generic">' + '<b:KeyValuePairOfstringanyType>' + '<c:key>Query</c:key>' + '<c:value i:type="b:FetchExpression">' + '<b:Query>';

request += CrmEncodeDecode.CrmXmlEncode(sFetchXml);

request += '</b:Query>' + '</c:value>' + '</b:KeyValuePairOfstringanyType>' + '</b:Parameters>' + '<b:RequestId i:nil="true"/>' + '<b:RequestName>RetrieveMultiple</b:RequestName>' + '</request>' + '</Execute>';

request += '</s:Body></s:Envelope>';

return this._ExecuteRequest(request, "Fetch", this._FetchCallback, fCallback);
}

FetchUtil.prototype._FetchCallback = function (xmlhttp, callback) {
///<summary>(private) Fetch message callback.</summary>
//xmlhttp must be completed
if (xmlhttp.readyState != XMLHTTPREADY) {
return;
}

//check for server errors
if (this._HandleErrors(xmlhttp)) {
return;
}

var sFetchResult = xmlhttp.responseXML.selectSingleNode("//a:Entities").xml;

var resultDoc = new ActiveXObject("Microsoft.XMLDOM");
resultDoc.async = false;
resultDoc.loadXML(sFetchResult);

//parse result xml into array of jsDynamicEntity objects
var results = new Array(resultDoc.firstChild.childNodes.length);
for (var i = 0; i < resultDoc.firstChild.childNodes.length; i++) {
var oResultNode = resultDoc.firstChild.childNodes[i];
var jDE = new jsDynamicEntity();
var obj = new Object();

for (var j = 0; j < oResultNode.childNodes.length; j++) {
switch (oResultNode.childNodes[j].baseName) {
case "Attributes":
var attr = oResultNode.childNodes[j];

for (var k = 0; k < attr.childNodes.length; k++) {

// Establish the Key for the Attribute
var sKey = attr.childNodes[k].firstChild.text;
var sType = '';

// Determine the Type of Attribute value we should expect
for (var l = 0; l < attr.childNodes[k].childNodes[1].attributes.length; l++) {
if (attr.childNodes[k].childNodes[1].attributes[l].baseName == 'type') {
sType = attr.childNodes[k].childNodes[1].attributes[l].text;
}
}

switch (sType) {
case "a:OptionSetValue":
var entOSV = new jsOptionSetValue();
entOSV.type = sType;
entOSV.value = attr.childNodes[k].childNodes[1].text;
obj[sKey] = entOSV;
break;

case "a:EntityReference":
var entRef = new jsEntityReference();
entRef.type = sType;
entRef.guid = attr.childNodes[k].childNodes[1].childNodes[0].text;
entRef.logicalName = attr.childNodes[k].childNodes[1].childNodes[1].text;
entRef.name = attr.childNodes[k].childNodes[1].childNodes[2].text;
obj[sKey] = entRef;
break;

default:
var entCV = new jsCrmValue();
entCV.type = sType;
entCV.value = attr.childNodes[k].childNodes[1].text;
obj[sKey] = entCV;

break;
}

}

jDE.attributes = obj;
break;

case "Id":
jDE.guid = oResultNode.childNodes[j].text;
break;

case "LogicalName":
jDE.logicalName = oResultNode.childNodes[j].text;
break;

case "FormattedValues":
var foVal = oResultNode.childNodes[j];

for (var k = 0; k < foVal.childNodes.length; k++) {
// Establish the Key, we are going to fill in the formatted value of the already found attribute
var sKey = foVal.childNodes[k].firstChild.text;

jDE.attributes[sKey].formattedValue = foVal.childNodes[k].childNodes[1].text;

}
break;
}

}

results[i] = jDE;
}

//return entities
if (callback != null) callback(results);
else return results;

}

 

function jsDynamicEntity(gID, sLogicalName) {
this.guid = gID;
this.logicalName = sLogicalName;
this.attributes = new Object();
}

function jsCrmValue(sType, sValue) {
this.type = sType;
this.value = sValue;
}

function jsEntityReference(gID, sLogicalName, sName) {
this.guid = gID;
this.logicalName = sLogicalName;
this.name = sName;
this.type = 'EntityReference';
}

function jsOptionSetValue(iValue, sFormattedValue) {
this.value = iValue;
this.formattedValue = sFormattedValue;
this.type = 'OptionSetValue';
}

showMainPhone.js

var _oService;
var _sOrgName = "";
var _sServerUrl = Xrm.Page.context.getServerUrl();

function fetchOnLoad()
{
// Get the ID of the Customer
var sCustGUID = Xrm.Page.getAttribute('
customerid ').getValue()[0].id;

var sFetch = "<fetch mapping='logical' count='10'>" +
"<entity name='account'>" +
"<attribute name='telephone1' />" +
"<filter type='and'>" +
"<condition attribute = 'accountid' operator='eq' value='" + sCustGUID + "'/>" +
"</filter>" +
"</entity>" +
"</fetch>";

_oService = new FetchUtil(_sOrgName, _sServerUrl);
_oService.Fetch(sFetch, myCallBack);
}

function myCallBack(results){
alert(results[0].attributes["telephone1"].value);
}

image

Now I have added my code to the incident entity. I also called my files by a somewhat different name; in this case “showMainPhone.js” is the same as “usingFetch.js”. The great thing here is the only text that really has to match up is the function name in the onLoad. Everything else was made pretty much bullet proof by CRM 2011.

Seeing the Results

When opening an incident, I will now see an alert box with the phone number of the account associated.

image

Granted, this may not be the particular feature you users are begging for (in fact the “showMainPhone.js” file needs plenty of error handling), but hopefully you can see how easy it is to embed a fetch call into your JavaScript. Whether you are summing up values from related entities or whatever, it is quite easy to use the fetchUtil.js file to enhance your end user’s experience.

Switching to Synchronous Fetch

This example used an asynchronous callback because that is what I would recommend most of the time. An asynchronous callback will let the browser execute other code; whereas the synchronous approach will wait for the fetch to finish. To use the synchronous version, you will need to change the “fetchOnLoad()” function to like so:

function fetchOnLoad() {

// Get the ID of the Customer
var sCustGUID = Xrm.Page.getAttribute('customerid').getValue()[0].id;

var sFetch = "<fetch mapping='logical' count='10'>" +
"<entity name='account'>" +
"<attribute name='telephone1' />" +
"<filter type='and'>" +
"<condition attribute = 'accountid' operator='eq' value='" + sCustGUID + "'/>" +
"</filter>" +
"</entity>" +
"</fetch>";

_oService = new FetchUtil(_sOrgName, _sServerUrl);
var res =_oService.Fetch(sFetch);
alert(res[0].attributes["telephone1"].value);

}


Post by: Paul Way, Customer Effective

CRM Banner

7 Responses to “Using the FetchXML CRM 2011 Service within a JavaScript Web Resource”

  1. Eugene says:

    Hi Paul,

    Forgive me for asking. Is there a mistake somewhere in the FetchUtil.js?
    As I was trying to understand the code, I’ve found this particular code with an open quote that wasn’t closed.

    You will find the code by CTRL+F the following:
    var sType = ”;

    Thanks for the codes though. Still in the midst of understanding it.

  2. HayWire says:

    Hi,
    Good Post.
    I wanted to ask if there is a way to check for null attributes returned by the fetch before accessing those attributes?? I really need to know this.
    Plz Help.

  3. Chris H says:

    Hi there. I have copied this into my custom JS and it works fantastic, but I’ve just run into a problem. I ran the new Custom Code validator that’s checking for items that won’t work across browsers, and it’s picking up this line.

    var resultDoc = new ActiveXObject(“Microsoft.XMLDOM”);

    ActiveXObject is apparently an IE Specific item, so it won’t work in the other browsers that CRM is due to support in the next few weeks/months.

    Have you thought about reworking this to be multi-browser compatible?

  4. Chris H says:

    I did a forum post an got a reply that I think will work, but it needs to be validated. Any glaring issues with using this??

    Post: http://social.msdn.microsoft.com/Forums/en-US/crmdevelopment/thread/e35d282f-1bbf-4b2b-8b25-e01a59ec65e6

    Code:
    // ===================================================
    // Old Code that is IE Specific
    // ===================================================
    var sFetchResult = xmlhttp.responseXML.selectSingleNode(“//a:Entities”).xml;
    var resultDoc = new ActiveXObject(“Microsoft.XMLDOM”);
    resultDoc.async = false;
    resultDoc.loadXML(sFetchResult);

    //parse result xml into array of jsDynamicEntity objects
    var results = new Array(resultDoc.firstChild.childNodes.length);
    for (var i = 0; i < resultDoc.firstChild.childNodes.length; i++) {
    var oResultNode = resultDoc.firstChild.childNodes[i];

    // ===================================================
    // New Code that is cross broswer compatible??
    // ===================================================
    var sFetchResult = xmlhttp.responseXML.selectSingleNode("//a:Entities");
    var resultDoc = sFetchResult.selectNodes("a:Entity");

    //parse result xml into array of jsDynamicEntity objects
    var results = new Array(resultDoc.length);
    for (var i = 0; i < resultDoc.length; i++) {
    var oResultNode = resultDoc[i];

  5. Christian says:

    I’m a little confused.
    First thing: When i copy the scripts in VS a lot of characters are unknown.
    For example ”.
    The second is, when i call usingFetch in OnLoad i get the message “Error:’FetchUtil’ is undefined

Ask This Expert a Question / Leave a Comment

 

 
 
Show Buttons
Hide Buttons