In the latest release of the Microsoft Dynamics SDK (Version 5.0.3) you will find a Microsoft Dynamics CRM Solution and Visual Studio extension that together will provide IntelliSense support for the Xrm.Page object in form scripts. I hope that this solution will help make your process of creating form scripts using the Xrm.Page object more productive. The result is that you can have IntelliSense that provides auto completion and documentation about the methods as shown below:
The SDK\Templates\Xrm.PageScriptProjectTemplate\Readme.docx file in the download package provides details about installing and using the solution. Rather than repeat what you can find there, in this post I will describe the process of creating this solution and I hope to set your expectations for what the solution offers.
The Microsoft Visual Studio team has been doing work to improve the developer experience while using JScript. There have been incremental improvements in Visual Studio 2008 and 2010. One of the key areas has been to improve the IntelliSense available for JScript. I saw the improvements that the Visual Studio team created for JQuery IntelliSense in VS 2008 and I hoped we could provide something like it for Jscript programming within Microsoft Dynamics CRM 2011.
My goal was simply to allow developers to get design time access to the documentation we publish in the Xrm.Page Reference content so that they don’t have to constantly refer to the documentation. However it wasn’t as simple as that. The Xrm.Page programming model is different from a library like JQuery. JQuery is a static library that you use to write code for any HTML page. When you write code that uses the Xrm.Page object found in a Microsoft Dynamics CRM form you are actually writing code to work on an in-memory object that is unique for each form and each entity. I eventually arrived at the conclusion that I could not create a generic helper ‘Xrm.Page-vsdoc.js’ file that developers could just add to a project.
How it was done
Visual Studio enables IntelliSense for JScript by instantiating an object in memory. (See JScript IntelliSense Overview for more information.) To make IntelliSense that would work for the Xrm.Page object found in a particular form I needed to find a way to capture all the data found in a form and use it to instantiate the same object for Visual Studio
The approach I finally settled on was to create a function that calls all the ‘get’ methods available for a form and to define that data as a single object variable using literal notation within a PageData.js file. This file is included in a Visual Studio solution. To get the data for a particular form you use a custom ribbon button on the form Customize tab. This button is labeled Xrm.Page Snapshot.
This button opens a dialog that allows you to capture the data from the form as shown below:
You have the option to specify a particular event if you intend to use the execution context, But it is not required. After you capture this snapshot you literally have to copy and paste the data from a dialog into the PageData.js file in your solution. In this way you can capture a snapshot of a form for any record or any state. All the available data for that record and the form is included. That was a lot of work, but actually relatively straightforward.
The more complex part is the creation of a library that would use the captured page data to instantiate an Xrm.Page object. This library also needs to include all the comments to support the reference documentation for the methods. The result was the XrmPageTemplate.js file. Almost 2500 lines of code were required for this.
Part of the thing that made this difficult was that the Xrm.Page object contains many objects where the type of methods available for an object depend on the ‘type’ of object. But because it is JScript the objects are not strongly typed. For the XrmPageTemplate.js file I needed to reverse engineer the Microsoft Dynamics CRM code to create JScript ‘classes’ in order to have a consistent structure for the object.
At first I tried to just generate a static object using literal notation. That process created a huge amount of code and just wasn’t going to be practical. Instead I finally created a mixed model where some of the Xrm.Page object was set using literal notation. Other repeated elements like attributes and controls were set using classes with a base class to set the prototype property.
Further complicating this is that collections in the Xrm.Page object includes methods that return different objects depending on the arguments passed to them. Each collection supports three methods: forEach, get, and getLength. The get method accepts null, string, number or a delegate function. Depending on the argument passed and the contents of each collection it can return null, an object, or an array. In order for IntelliSense to provide useful information about objects returned by these collection methods I would need to get an instance of the object that would be returned for that particular method.
An unexpected benefit
The point of elaborating on all the difficulties I encountered in generating useful IntelliSense for Xrm.Page objects is that it explains why I ended up going one step further. The additional work opened up a new use for the project – testing your code outside of Microsoft Dynamics CRM.
Because I ended up reverse engineering most of the methods to return objects it wasn’t that much more work to commit to recreating the functionality for virtually all the public methods in the Xrm.Page object. The result is that you can actually use the Xrm.Page object within a test html page in Visual Studio to perform some initial testing of your functions before you create Web Resources in Microsoft CRM. This is a distinct advantage because frankly it is easier to debug JScript within a HTML page in Visual Studio than within a Microsoft CRM form.
The TestPage.htm within the Xrm.Page JScript Library Project doesn’t attempt to re-create the visible elements of the CRM form, however because all the elements of the Xrm.Page object are available your code will work. You can write simple unit test code within the TestPage.htm onload event to verify that your code works. For example, if your function will call the setVisible method for a form section, you can write test code to use the getVisible method and display a message to confirm that it worked as expected. If you want to test a function for a different record, just replace the page data with data you copy from a different record or an unsaved form.
The other ‘benefit’ of using the TestPage.htm for testing is that it requires that you only use supported methods of the Xrm.Page object. You can’t resort to using getElementById because the HTML page doesn’t contain an object you can reference. You have to stick to supported methods. The downside of this method of testing is that you can’t interact programmatically with content in Iframes or Silverlight controls that may are embedded in your form. You will still need to test those capabilities within the CRM form itself.
Testing the code within the project has its limitations. You may not be interested in testing your code this way and that’s fine. You can still use the project to just enjoy the benefits of IntelliSense.
One problem that still requires a bit of a workaround is writing functions that reference the execution context object. (See the SDK topic Use Execution Context and the Form Event Pipeline). Your functions have to be written so that the execution context is passed as the first parameter to the function. Setting this parameter in your function will initialize the object, effectively breaking IntelliSense for the event context object in your function. The workaround for this is to simply define the parameter after you have finished writing the function. If you need it, the Xrm.Page Snapshot will generate a global eContext object that you can use for IntelliSense as you first write the function. However you need to remember to add an eContext parameter to your function before you test it.
Nothing is perfect
Finally, I want to acknowledge that despite all the work in preparing this solution there are some aspects I’m not fully satisfied with. Visual Studio IntelliSense for JScript just doesn’t seem to be as robust as IntelliSense for managed code. For very simple functions the IntelliSense works fine. But for more complex functions the IntelliSense will eventually stop working. This may just be the nature of an interpreted language or it may be due to the way I’ve factored my code. I worked with the Visual Studio developers and tried several different approaches to factor the code but you definitely find that at some point Visual Studio will stop displaying IntelliSense. This doesn’t’ necessarily mean that your code is not correct. In fact you can test it using the TestPage.htm and in the form to confirm that it works, but you don’t get the full IntelliSense all the time. In the Readme file I’ve documented those cases I know will cause the IntelliSense to stop working. Despite this, I still think the project offers a significant advantage over writing your Jscript libraries without it.
What do you think?
If you have feedback or questions about this solution please send e-mail to :
Feedback for JScript IntelliSense Solution