Monday, September 1, 2008

Ajax using Fetch Message


Ajax is a collection of technologies complied into one word (“mess”), as such retrieving information from crm can be cumbersome and error prone. Although there are tools that can help you accomplish the task I found they require too match code and sometimes you find your self wasting hours looking for meaningless errors.


Now, all I wanted is; to create a fetch (using advanced find), hand it over to a js method (Fetch) and expect an xmldocument (FetchResult) I can query with ease. The code below is the result of my quest “to the promised land”.



To get the FetchXml, All you have to do is:

1. Create an advanced find query using ,well, crm advanced find , press ctrl + N to open the advanced find in a new window to reveal the address bar (or press F11 twice)


2. In the address bar paste this simple js snippet that returns the
FetchXml:
javascript:void( alert( advFind.FetchXml ))


3. Press ctrl + A and ctrl + c to copy the fetch into your working js document or onload event.


The CreateXmlHttpObject() and FetchEncode() are functions Microsoft use in their code. I pasted them here as part of the infrastructure to avoid dependency. All you really need is the Fetch() function. Now this is unsupported but because the code is considered infrastructure you
should consider putting it inside global.js and make it available to all crm Forms (entities).


If MS is listening, one of the most needed features ,beside filtered lookups, is an "OnGlobal" event (like the onload event) that integrates code as such to the global.js file.


The example below uses a static fetch that returns the current user; you may concatenate dynamic values as needed.




function OnCrmPageLoad()
{
var FetchResult = Fetch('<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="false"><entity name="systemuser"><attribute name="fullname"/><attribute name="businessunitid"/><attribute name="title"/><attribute name="address1_telephone1"/><attribute name="systemuserid"/><order attribute="fullname" descending="false"/><filter type="and"><condition attribute="systemuserid" operator="eq-userid"/></filter></entity></fetch>');
alert( FetchResult.selectSingleNode("//fullname").text ); //User Fullname
}

function Fetch( xml )
{
var Xml = "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">"
Xml += GenerateAuthenticationHeader()
Xml += "<soap:Body>";
Xml += "<Fetch xmlns=\"http://schemas.microsoft.com/crm/2007/WebServices\">";
Xml += "<fetchXml>";
Xml += FetchEncode(xml); // Microsoft _HtmlEncode function
Xml += "</fetchXml>";
Xml += "</Fetch>";
Xml += "</soap:Body>";
Xml += "</soap:Envelope>";

var XmlHttp = CreateXmlHttpObject(); // Microsot CreateXmlHttp function
XmlHttp.open("POST", "/mscrmservices/2007/crmservice.asmx", false ); //Sync Request
XmlHttp.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
XmlHttp.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/crm/2007/WebServices/Fetch");
XmlHttp.send(Xml);

var XmlDoc = new ActiveXObject("Msxml2.DOMDocument");
XmlDoc.async = false;
XmlDoc.resolveExternals = false;
XmlDoc.loadXML(XmlHttp.responseXML.text);

return XmlDoc;
}

function CreateXmlHttpObject() //CreateXmlHttp
{
var oXmlHttp = null;
if(window.XMLHttpRequest)
{
oXmlHttp = new XMLHttpRequest();
}
else
{
var arrProgIds = ["Msxml2.XMLHTTP","Microsoft.XMLHTTP"];
for(var iCount = 0; iCount < arrProgIds.length;iCount++)
{
try
{
oXmlHttp = new ActiveXObject(arrProgIds[iCount]);
break;
}
catch(e){}
}
}

return oXmlHttp;
}

function FetchEncode( strInput ) //_HtmlEncode
{
var c;
var HtmlEncode = '';

if(strInput == null)
{
return null;
}
if (strInput == '')
{
return '';
}

for(var cnt = 0; cnt < strInput.length; cnt++)
{
c = strInput.charCodeAt(cnt);

if (( ( c > 96 ) && ( c < 123 ) ) ||
( ( c > 64 ) && ( c < 91 ) ) ||
( c == 32 ) ||
( ( c > 47 ) && ( c < 58 ) ) ||
( c == 46 ) ||
( c == 44 ) ||
( c == 45 ) ||
( c == 95 ))
{
HtmlEncode = HtmlEncode + String.fromCharCode(c);
}
else
{
HtmlEncode = HtmlEncode + '&#' + c + ';';
}
}

return HtmlEncode;
}

OnCrmPageLoad();

9 comments:

Anonymous said...

Thank you. This is such a great work. May you have a happy life :)

Anonymous said...

Thank you, Adi. You did a great job. But can you show me how to fetch on dynamic values? e.g. on a lookup. I couldn't get it work. Thanks.

Adi Katz said...

Hi,

Replace the condition that calls the Fetch method : <condition attribute="systemuserid" operator="eq-userid"/>

With

<condition attribute="systemuserid" operator="eq" value="' + crmForm.ownerid[0].id + '"/>

Where ownerid is the name of the lookup field and the id is the actual guid.

Anonymous said...

I still use this technique...one of the best blog posts on Dynamics CRM I've seen...excellent work!

Anonymous said...

Thannk you, Adi!

Anonymous said...

Hi Adi,

I followed your instructions but still can't get the fetch to work in crm40 to retrieve more fields..I have a crmForm.all.equipmentid which is a lookup field and try to use your dynamic method

condition attribute="systemuserid" operator="eq" value="' + crmForm.all.new_equipmentid[0].id + '"/

it give an error of "object expected".. tried crmForm.new_equipmentid.DataValue[0].id and crmForm.new_equipmentid[0].id as well

it works fine when I substituted with the actual equipmentid like "13245" but that's not dynamic

thanks.

Anonymous said...

btw, great posts Adi, definitely agreed on being one of best crm blogs.

Dave Berry said...

Adi, you forgot to include the GenerateAuthenticationHeader() code for dependency. Otherwise, I appreciate the enlightenment obtained from the article about implementing Fetch in Javascript, and have used your example to my benefit. Thank you!

Unknown said...

Thank you Adi. I am a big fan and follower of your posts and this was really nice and simple and efficient.

Thanks for posting!
--Srihari.