Pages

Showing posts with label Jscript. Show all posts
Showing posts with label Jscript. Show all posts

Tuesday, January 15, 2013

Duplicate Field Mapping/Defaulting


I encountered a scenario where whenever a child entity is created from within the context of a parent entity, the mapping automatically creates two references from the parent entity on the child entity.


The reason for this is due to the fact that the parent entity is related in a 1:N relationship to the child entity more than once:


And each of these relationships automatically creates a mapping that resembles the one below. That is both mapping contain a link from the parent entity to each of the foreign keys from the child entity and you are unable to delete these as they are required by the CRM platform.


Moreover any mapping added to one of the relationships is automatically added to the other relationship (and vice versa). I can't figure out if this is absolutely necessary from a technical design perspective or if this is just a design flaw. I suspect the latter because I can't come up with a good reason for this design logic. Or it could just be something specific that managed to sneak into the environment I'm working in.

The major issue is that this results in a confusing design because both fields get defaulted when only one should depending on the navigational context i.e.:
  • When navigating from "Entities Owned" the "Entity" field should be defaulted from the parent entity whereas the "Is Invested In" entity should remain null
  • Conversely, when navigating from "Investors in this Entity" the "Is Invested In" field should be defaulted from the parent entity whereas the "Entity" should remain null



As standard field mapping seems to rule out the ability to differentiate between these two different contexts, the only way to do so would seem to be by passing in custom parameters which is an example of when this technique is absolutely required.

To do so, the form was modified to remove the default "Add New" buttons and replace with custom Add buttons (which as an aside also provides the ability to give much clearer names to the add operation). The Ribbon Workbench Tool ably supports this kind of configuration. Each of these buttons simply calls the Xrm.Utility.openEntityForm to open the same child form button, but passes in parameters to set the lookup fields as required in each case.



Access Main form fields from Nav Area via Jscript

Let's say you need to access form fields via jscript when you have navigated to one of the navigational links on the entity. For example, when the account form is open but you are currently on the "More Addresses" navigation.


A typical scenario for such a requirement is if you have configured a custom button on the sub-grid ribbon that links to a jscript function where you need to pass through some parameters that are set from fields on the main form.

If you try and access the fields using the standard Xrm.Page approach while in the above navigational scenario, your jscript will error out. Like so:

function CustomAction() {

 var parameters = {};
 parameters["snt_customid"] = Xrm.Page.data.entity.getId();
    parameters["snt_customidname"] = Xrm.Page.data.entity.attributes.get("snt_name").getValue();
 
 Xrm.Utility.openEntityForm("snt_custom", null, parameters);

}

The solution is pretty much the same as how page elements are referenced from html web resources linked to a form i.e. using the prefix of "window.parent". Therefore updating the function as follows should do the trick:

function CustomAction() {

 var parameters = {};
 parameters["snt_customid"] = window.parent.Xrm.Page.data.entity.getId();
    parameters["snt_customidname"] = window.parent.Xrm.Page.data.entity.attributes.get("snt_name").getValue();
 
 Xrm.Utility.openEntityForm("snt_custom", null, parameters);

}

Update:

The above design has one flaw and that is if you decide to use a sub-grid to bring in the navigation link into the main body of the form. When you do so, the same sub-grid menu shows up when you click on the sub-grid but there is one essential difference...



In the case of the former, the main form is no longer in view and therefore you cannot access the form elements (Xrm.Page.data) without using the "window.parent" prefix. I you don't use this prefix you will receive a jscript errror.

In the case of the latter the main form is still in view and therefore Xrm.Page.data is accessible. And therefore if you use the "window.parent" prefix you will receive a jscript error.

In order to cater for both scenarios the solution is quite simple - simply use the try/catch exception handling feature. This way if an exception occurs it will go to the alternate access method which will then succeed.

function CustomAction() {

 var parameters = {};
 var id;
 var name;
 try {
  // if called from Nav Link navigation
  id = window.parent.Xrm.Page.data.entity.getId();
  name = window.parent.Xrm.Page.data.entity.attributes.get("snt_name").getValue();
 } catch (e) {
  // if called from Embedded Grid navigation
  id = Xrm.Page.data.entity.getId();
  name = Xrm.Page.data.entity.attributes.get("snt_name").getValue();
 }
 parameters["snt_customid"] = id;
 parameters["snt_customidname"] = name;

 Xrm.Utility.openEntityForm("snt_custom", null, parameters);
}

Wednesday, December 26, 2012

getClientUrl() to replace getServerUrl()

It appears that the javascript getServerUrl() function will become deprecated with the release of Update Rollup 12 for CRM Dynamics 2011 (imminent). As far as I can tell, from that point forward there will be a new function called getClientUrl() that should be used instead.

No doubt the reason for the deprecation is due to the fact that the getServerUrl() function cannot always be relied upon to return the correct server context as described in an earlier post (and therefore the workaround solution described in that post should no longer be necessary).

We'll have to get back to this to confirm once the rollup has been officially released.

Form Query String Parameter Tool

The Dynamics team recently released a new utility called the Form Query String Parameter Tool. Essentially it's a useful little utility that can be used to generate the syntax of a create form URL passing in parameters to default field values when the new form is opened.

Having said that, I believe that in in most cases using the URL parameter passing approach for defaulting values on the create form should not be necessary. The rest of this post will explain this point of view.

First of all, the out of the box "field mapping" approach - whereby field values from the currently open parent entity are mapped to the child entity form being opened - should be used where ever possible. This covers scenarios where the default values are static regardless of the form "context" and the field being mapped is in fact "mappable".

If the default values are dependent on the form "context" (e.g. new accounts where type = "customer" should have different defaults than cases where type = "vendor") or if the field being mapped is not "mappable" then your next best bet is using javascript on the form load event to set the defaults. Using javascript you can set all the appropriate fields as long as you have a field populated on the form in order to make the necessary branching default logic (the form "context" field). The form context field may be set using either standard field mapping, a web service API call (as explain below) or via URL parameters. Once obtained the javascript default logic can take over.

Using the URL parameters approach would therefore only seem to be necessary when you don't have a form "context" field on the form that is being opened. However this is not necessarily the case either since you can also leverage the web service calls (RESTful or SOAP) in order to retrieve the "context" from the parent field in order to perform the necessary branch logic.

Therefore it would seem that using form parameters to set default values would be necessary to only a fairly limited set of scenarios. This would be a scenario where all 3 conditions listed below are true:


  • The fields cannot be defaulted via standard parent/child field mapping
  • The form does not contain a form "context" field on which to base javascript branching logic
  • A standalone form that is not linked to another form from which the "context" can be retrieved using web services


Finally, even in the remaining few scenarios that do require the URL mapping approach, it is really only necessary to pass through a single URL parameter - that will set the context and javascript can subsequently take over for the remaining default logic.

Therefore, when all is said and done, while it's always nice to see new tools being developed to facilitate the customization effort, I do not see myself using this particular one all too frequently.

Perhaps there are scenarios that I'm not considering? If I encounter them I'll be sure to dish.

One scenario encountered is with differentiating between multiple 1:N field maps for the same two entities as described in this post.


Friday, November 2, 2012

Passing Execution Context to Onchange Events

In a previous posting I provided some jscript that can be used to validate phone number formats. In order to invoke the validation for a phone number field I mentioned that you need to create an "on change" event that would pass in the attribute name and attribute description i.e.:

function Attribute_OnChange() {
PhoneNumberValidation("attributeName", "attributeDescription");
}

I thought this would provide a good example for demonstrating the ability to use the execution context because you can obtain the field name and label (by extension) via the execution context which on the surface is a good thing since you avoid hard-coding as in the example above (and you can apply this to all phone number fields in the system). And therefore in theory you could simplify that example as follows:


function PhoneNumberValidation(context) {
var phone = context.getEventSource().getName();
var phoneDesc = Xrm.Page.getControl(context.getEventSource().getName()).getLabel();
 var ret = true;
 var phone1 = Xrm.Page.getAttribute(phone).getValue();
 var phone2 = phone1;
 
 if (phone1 == null)
  return true;
  
 // First trim the phone number
 var stripPhone = phone1.replace(/[^0-9]/g, '');

 if ( stripPhone.length < 10 ) {
  alert("The " + phoneDesc + " you entered must be at 10 digits. Please correct the entry.");
  Xrm.Page.ui.controls.get(phone).setFocus();
  ret = false;
 } else {
  if (stripPhone.length == 10) {
   phone2 = "(" + stripPhone.substring(0,3) + ") " + stripPhone.substring(3,6) + "-" + stripPhone.substring(6,10);
  } else {
   phone2 = stripPhone;
  }
 
 }
 Xrm.Page.getAttribute(phone).setValue(phone2);
 return ret;
}

The only difference is that instead of the "phone" and "phoneDesc" parameters being passed into the validation function, the execution context is instead passed in and the phone attribute and its corresponding phoneDesc label are obtained via the context as local variables. The rest stays the same.

In order for this to work, you would update the "on change" event to call the PhoneNumberValidation function directly and check off the "pass execution context as first parameter" as shown:




So that's the theory and I think it demonstrates quite nicely how the execution context can be used. 

Having said that, in this particular example, I prefer using the explicit technique referenced in the original posting. The reason for this is because the on change event in this validation example (and probably relevant for most data validation cases) has a dual function - 

The first is to provide the necessary validation as part of the field on change event as the example above will accomplish quite well. 

The second is to be called from the on save event to make sure that even if users ignore the message from the on change event they will not be able to save the form via the validation from the on save event (the PhoneNumberValidation function returns a true or false value to indicate whether validation was passed or not). And when the function is called from the on change event the specific field context is not going to be there anyway making it necessary to put in some additional logic in order to handle correctly. Therefore what you gain from using the execution context in this example is likely to be offset by requirements for special handling required by the on save event. 

Monday, October 29, 2012

JSON vs. Ajax vs. jQuery - layman's guide

JSON, Ajax, and jQuery are all technologies that are frequently referenced and implemented to provide a great deal of flexibility and data manipulation options when customizing Dynamics CRM - in particular as it relates to Jscript customization. This post is therefore dedicated to providing a very brief layman's guide to each of these technologies and what the essential difference and function of each is.

JSON

JSON is simply a data format much like XML, CSV,  etc. Its primary function is to provide an alternative to the XML standard. For example (borrowed from Wikipedia), an XML format for a "person" entity might be as follows:


<person>
  <firstName>John</firstName>
  <lastName>Smith</lastName>
  <age>25</age>
  <address>
    <streetAddress>21 2nd Street</streetAddress>
    <city>New York</city>
    <state>NY</state>
    <postalCode>10021</postalCode>
  </address>
  <phoneNumbers>
    <phoneNumber type="home"> 212 555-1234</phoneNumber>
    <phoneNumber type="fax">646 555-4567</phoneNumber>
  </phoneNumbers>
</person>


Whereas the JSON equivalent would be as follows:

 {
    "firstName": "John",
    "lastName": "Smith",
    "age": 25,
    "address": {
        "streetAddress": "21 2nd Street",
        "city": "New York",
        "state": "NY",
        "postalCode": "10021"
    },
    "phoneNumber": [
        {
            "type": "home",
            "number": "212 555-1234"
        },
        {
            "type": "fax",
            "number": "646 555-4567"
        }
    ]
}

The benefit cited for JSON over the XML standard is that JSON is generally considered to be lighter weight and easier to process programmatically while maintaining all the other "aesthetic" benefits of the XML standard. For a more detailed analysis of these benefits, refer to the JSON web site.


Ajax 

Ajax is used for asynchronous processing of web pages. Meaning that once the web page has been loaded Ajax can be used to interact with the server without interfering with the already rendered web page i.e. such requests happen as part of background processing. Ajax is therefore typically used to make web pages highly interactive without having to reload the web page every time a new server request is made to retrieve data based on user interaction. A classic example is when you start typing an airport name at one of the online reservation sites and the drop down list shows relevant options based on what you are entering.

Ajax interacts with the server by means of an XMLHttpRequest object. And although the results that are retrieved can be in XML format (as is indicated by the object name) it is typically more common to retrieve the results in JSON format as that is easily consumed by JScript.


jQuery

jQuery is meant to simplify JScript programming by making it easier to to navigate, handle events, animate, and develop Ajax for web pages. The latter being most relevant for this summary. And therefore as far as
Ajax is concerned - jQuery leverages Ajax for performing server requests and simplifies interaction with the Ajax layer. Put simply, Ajax is the tool that jQuery will use for handling asynchronous server requests. So when you see the "$.ajax" method in your JScript code it means that jQuery ($.) is being used to execute an Ajax request (ajax).

At this point in time, only the jQuery ajax method is supported by Dynamics CRM. Or to quote from the SDK:

The only supported use of jQuery in the Microsoft Dynamics CRM 2011 and Microsoft Dynamics CRM Online web application is to use the jQuery.ajax method to retrieve data from the REST endpoint. Using jQuery to modify Microsoft Dynamics CRM 2011 application pages or forms is not supported. You may use jQuery within your own HTML web resource pages.

Monday, October 15, 2012

Ribbon: Get View ID (Context Sensitive)

Let's say you want to execute an action that will act on the view that is currently being viewed i.e. the objective is that the action will be context sensitive to the current view. How can this be achieved?

The first thing to do is of course to add the a button to the application ribbon and you'd be well advised to use the Ribbon Workbench Tool to do this. As we are adding a button that will appear in the grid views you'll need to be sure to select the HomePage option.


As illustrated in the screenshot above, the action for the ribbon button references a JavaScript function, so you will of course need to create a JScript web resource and a custom function to match what you define for the ribbon action command.

The JScript function will then take care of launching the custom action and passing in the View ID of the view currently being viewed - which is the crux of this post. The script below provides the method for obtaining the view ID:

function GetViewID() {
    try {
        if (document.getElementById('crmGrid_SavedNewQuerySelector')) {
            var view = document.getElementById('crmGrid_SavedNewQuerySelector');

            var firstChild = view.firstChild;

            var currentview = firstChild.currentview;
            if (currentview) {
                var viewId = currentview.valueOf();
    alert(viewId);
            }
        }
        else {
            alert("No Element");
        }
    }
    catch (e) {
        alert(e.message);
    }
}

The result of the above exercise will be a button on the ribbon that when clicked will pop up with the ID of the current view:


You can of course tailor this jscript to pass in the retrieved View ID to whatever custom action you wish to execute.

Friday, October 12, 2012

context.getServerUrl() not getting correct context

Whenever you need to run a web service or fetch query from within the jscript of the form you need to first obtain the server URL of your environment. Typically that is performed by running the following set of commands:

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

However there are a few issues with the context that this returns. For example, if I open up a contact form that has some fetch logic on the CRM server, then form opens up and renders cleanly. However opening the same contact form remotely will result in an error such as the one shown below:



The reason for this is quite simple:

When running CRM from the server, the URL does not need to include the domain name. For example: http://crm/org1. However when running from a client machine you need to specify (in certain scenarios) the full domain in order for CRM to open e.g. http://crm.acme.com/org1.

The problem with the context.getServerUrl() command is that in both of the above cases it will return http://crm/org1 (without the acme.com) which is going to be valid when running from the server but not when running from a remote client.

Although I have not confirmed I believe there will be similar issues if you're running CRM over https i.e. it will cause browser security warning popups to be issued as the command will try and execute over the http context rather than https.

The solution that we found (thanks to this post) was instead to use the following syntax in favor of the more standard formula listed above:

var context = Xrm.Page.context;
var serverUrl = document.location.protocol + "//" + document.location.host + "/" + context.getOrgUniqueName();

The result is that when running CRM using the http://crm/org1 URL, serverUrl will return http://crm/org1. And when running CRM using the more fully qualified http://crm.acme.com/org1, serverUrl will correspondingly return http://crm.acme.com/org1. Which of course is what you should expect.

And this small tweak results in the experience being the same no matter which machine CRM is accessed from.

Thursday, October 11, 2012

Phone Number Formatting

It is a fairly common and understandable requirement that telephone numbers be formatted when entering via the CRM front end. For example, if a user enters "5551234123" or "555-1234123" or "555-123-4123" or any other variation thereof it should be formatted into the standard format of  "(555) 123-4123".

The  jscript logic mentioned below can be used to this end. Specifically this logic will work as follows:
  • Strip any non-numeric values from the phone number field
  • If the phone number is less than 10 digits issue a warning 
  • If the phone number is equal to 10 digits, format as in the example above
  • If the phone number exceeds 10 digits assume it to be an international number and do not perform any additional formatting (besides stripping non-numeric values)
  • Prevent the form from being saved unless the phone numbers adheres to the above rules

The steps to achieve this are as follows --

First, define the PhoneNumberValidation function that performs all the heavy lifting:

function PhoneNumberValidation(phone,phoneDesc) {
 ret = true;
 var phone1 = Xrm.Page.getAttribute(phone).getValue();
 var phone2 = phone1;
 
 if (phone1 == null)
  return true;
  
 // First trim the phone number
 var stripPhone = phone1.replace(/[^0-9]/g, '');

 if ( stripPhone.length < 10 ) {
  alert("The " + phoneDesc + " you entered must be at 10 digits. Please correct the entry.");
  Xrm.Page.ui.controls.get(phone).setFocus();
  ret = false;
 } else {
  if (stripPhone.length == 10) {
   phone2 = "(" + stripPhone.substring(0,3) + ") " + stripPhone.substring(3,6) + "-" + stripPhone.substring(6,10);
  } else {
   phone2 = stripPhone;
  }
 
 }

 
 Xrm.Page.getAttribute(phone).setValue(phone2);
 return ret;
}

Then for each attribute on the form you wish to format, create an "on change" function as follows:

function Attribute_OnChange() {
PhoneNumberValidation("attributeName", "attributeDescription");
}

Where:
  • attributeName is the name of the attribute the "on change" function is acting on e.g. telephone1
  • attributeDescription is a the friendly name for the attribute (used for the warning error messages)

Finally, reference the "on change" event from the form "on save" event to prevent the form from saving unless the phone number has the correct format. For example, the following will work:
function Form_onsave(executionObj) {

 var val = true;
 if (!Attribute_OnChange()) 
  val = false;
 
 if (!val) {
  executionObj.getEventArgs().preventDefault();
      return false;
 }

}

Don't forget to check the "pass execution context as first parameter" in the "on save" event or otherwise it will not work!


Friday, September 14, 2012

CRM Outlook Form and Display Rules

There seems to be an issue with the Outlook CRM create form as it relates to the Display Rule for ribbon buttons. More specifically - and by way of example - you can add a custom button to the ribbon and set it up with a Display Rule to control when the button should show. For instance, you can set up a FormStateRule where State = Existing and this will tell the application that the custom button should only show on the edit (update) form and not the new (create) form.

So where's the issue?

Well in addition to the Display Rule, you can also define an Enable Rule which controls whether the button should appear in an enabled or disabled (i.e. greyed out) state. The Display Rule trumps the Enable Rule i.e. if the Display Rule returns a "Do not display" result, then there's no point in executing the Enable Rule to determine how to display it. Which of course makes sense and is how it should work.

The good news is that this is precisely the behavior if you open up the create form using Internet Explorer navigation. Yay!

The bad news is that it seems that the same behavior is not exhibited if you open up the create form using Outlook navigation. And just to make sure you know what I mean by "Outlook navigation" below is a screenshot to help clarify this:

Outlook Create Form - note the icon at top left

Versus the same form opened using Internet Explorer:

IE Create Form - note IE container

The issue of course is that in the case of the Outlook Create Form it will execute the Enable Rule function whereas in the IE Create Form it will not. And the result might be an error in the Outlook Create Form as the function may not be relevant to such a form state.

Fortunately there is a work around until Microsoft fixes this issue (which I haven't made them aware of as the workaround is quite effective and not too onerous). Simply add a condition to your custom jscript function to limit it to working with update form as highlighted in the example below.

function CustomButtonEnableRule() {

 if (Xrm.Page.ui.getFormType() == 2) {
     // function logic
 } else {
     return false
 }
}

Friday, July 20, 2012

New Xrm.Utility Functions in Update Rollup 8

Apparently roll up 8 brought along with it 2 new client side Xrm.Utility scripting functions along with it. These functions are described in the MSDN blog posting.

The openEntityForm function seems interesting and would seem to replace the standard javascript "window.open" function specifically as it relates to opening CRM entity forms. So something that could have been previously achieved with the likes of this script:

window.open(Xrm.Page.context.getServerUrl() +
 "/sfa/conts/edit.aspx?id=" + Xrm.Page.getAttribute("primarycontactid").getValue()[0].id,
"MyWindow",
"toolbar=0,resizable=1,width=screen.width,height=screen.height");




Can now be achieved with a simpler and presumably cleaner (in terms of user experience):

Xrm.Utility.openEntityForm("contact", Xrm.Page.getAttribute("primarycontactid").getValue());

The more interesting aspect of this command is the ability to pass parameters when opening the form (probably more apropo to the create form). This probably lends itself to creating a "clone" action for every CRM form among other more obvious usages.

The second function (openWebResource) is less obvious in terms of its usefulness - at least to me at this juncture. Perhaps it can optimize the performance for opening a form by not having to specify all web resources in the form definition that the form might use during the course of its operation and you can then manually load a web resource in the jscript when it is necessary? I'd be interested to see what the various use cases for this script might be.

Time to experiment and find out...

Wednesday, June 20, 2012

Read Optimized Forms

I have been wanting to check this new feature out for a while that was introduced along with update rollup 7. There is a pretty decent write up of this new feature and how it can be enabled in the MSDN blogs.

While this feature is interesting and can further optimize the time it takes to open a form there are some important limitations. That is if you scroll down to the bottom of the article it contains a table showing when the "read optimized form" is loaded. And you will quickly realize that if a form has client side scripts it cannot take advantage of this feature.

I tested this out. I took a completely vanilla invoice form and - as advertised - when I opened the record it opened in the nice and clean Read Optimized format.



I then went ahead and added a jscript web resource to the form. To be clear I just added the web resource that are "available to the form", I did not actually wire up any OnLoad, OnSave, or OnChange functions. That is, this web resource was effectively doing nothing.




After I published this, I onced again opened up the invoice form and the Read Optimized setting was no longer respected i.e. it opened in full edit mode. In short, the mere presence of a jscript resource will make the form non-Read Optimized capable.

I am sure there are reasons why it was designed this way. There is certainly some jscript logic that can be impeded by the Read Optimization. Although I guess I'm wondering why Microsoft couldn't have introduced a 7th form type (in addition to create, update, read, disabled, quick, and bulk) in order to target this form option rather than taking the "all or nothing" approach. Perhaps that will be accomodated in a future enhancement or perhaps I haven't properly considered the technical issues that would accompany such a feature...

Anyway, in terms of the practical usage of this feature in it's current incarnation - I have to say I'm a little skeptical.

First of all, it has to be said - what percentage of forms have no jscript acting on them? Jscript really brings tremendous flexibility to form rendering etc. to the extent that not many forms (at least the ones that I touch) remain jscript-less. And even for those that do not have any jscript acting on them - it is of course quite likely over the course of time that you'll want to add some scripting to the form to accomodate some customer requirement. And at this juncture - assuming the client has been happily using Read Optimization for this form - you'll have to explain to the client that this feature that they've now come to know and love will no longer be available.

So when all is said and done, I think I'll personally be employing this feature quite sparingly.

Monday, June 18, 2012

Synchronous Retrieve Multiple Query

We have covered how to go about constructing retrieve queries for the following cases:

So we need to close out this topic by providing how to go about constructing a synchronous retrieve multiple query. Once again, synchronous queries are necesssary where there is jscript dependency logic.

So let's get right to it.

First of all place the following function into one of your form jscript resources:

function retrieveMultipleSync(entity,filter,fields) {

    try {
  var context = Xrm.Page.context;
  var serverUrl = context.getServerUrl();
  var query = new XMLHttpRequest();
  var oDataSelect = serverUrl + "/XRMServices/2011/OrganizationData.svc/" + entity + "Set"+fields+"&$filter="+filter;
  query.open('GET', oDataSelect, false);
  query.setRequestHeader("Accept", "application/json");
  query.setRequestHeader("Content-Type", "application/json; charset=utf-8");
  query.send(null);
  return JSON.parse(query.responseText).d.results;
 } catch (e) {
  alert("Retrieve multiple failed to return results");
 }

}


The next step is to call the above function to retrieve the values you are looking for by passing the following parameters:
  • entity - the entity logical name e.g. Product, Contact, Account
  • filter - a valid filter clause which will retrieve 1 or more records from the entity (see example)
  • fields - you wish returned from the retrieve query. NB: These are case sensitive so be sure to ensure to check how they are defined on the entity. You also can use the OData Query tool to get the syntax. Errors encountered with configuring this are most likely to be in this area.  

The following is a sample call to the retrieveMultipleSync function:

 var entity = "new_contact_systemuser";
 var entityid = Xrm.Page.data.entity.getId();
 var filter = "contactid eq guid'" + entityid + "'" + " and "+  "systemuserid eq guid'" + Xrm.Page.context.getUserId() + "'";
 var fields = "?$select=systemuserid";
  
 var entityData = retrieveMultipleSync(entity,filter,fields);
 if (entityData.length > 0)
  return false;
 else 
  return true;

And that's a wrap!

Friday, June 15, 2012

Synchronous Retrieve Query

Previously I provided an approach for constructing retrieve queries in CRM 2011. That approach used an asyncronous technique for running the query. That is, the form jscript does not wait for the results of the query to be obtained but instead continues to process with the query being executed in a separate thread. The benefit to this kind of approach is improved performance. For example, if a form field needs to be set then in most cases using an aysnchronous query should work (as long as that query doesn't take too long to execute).

However in many cases it is also necessary to run such queries syncronously in order to accomodate dependency logic in the jscript. The following provides a technique for constructing the synchronous version of this query (please refer to prerequisites mentioned in my previous post).

First of all place the following function into one of your form jscript resources:

function retrieveEntityByIdSync(entity,entityid,fields) {

    try {
		var context = Xrm.Page.context;
		var serverUrl = context.getServerUrl();
		var query = new XMLHttpRequest();
		var oDataSelect = serverUrl + "/XRMServices/2011/OrganizationData.svc/" + entity + "Set(guid'" + entityid + "')" + fields + "";
		query.open('GET', oDataSelect, false);
		query.setRequestHeader("Accept", "application/json");
		query.setRequestHeader("Content-Type", "application/json; charset=utf-8");
		query.send(null);
		return JSON.parse(query.responseText).d;
	} catch (e) {
		alert("Retrieve multiple failed to return results");
	}

}

The next step is to call the above function to retrieve the values you are looking for by passing the following parameters:
  • entity - the entity logical name e.g. Product, Contact, Account
  • entityid - the id of the passed in entity
  • fields - you wish returned from the retrieve query. NB: These are case sensitive so be sure to ensure to check how they are defined on the entity. You also can use the OData Query tool to get the syntax. Errors encountered with configuring this are most likely to be in this area.

For example, the following will retrieve the first and last name from the system user entity using a synchronous query:

	var e1 = retrieveEntityByIdSync("SystemUser",Xrm.Page.context.getUserId(),"?$select=FirstName,LastName");
	alert(e1.FirstName);
	alert(e1.LastName);


Sunday, April 8, 2012

Constructing a Retrieve Query in CRM 2011

CRM 2011 provides the ability to retrieve information from other entities when loading up a CRM form. For the most part, this was achieved using 3rd party add-ons in CRM 4.0. This post will attempt to provide a simple, practical approach for constructing a retrieve query in CRM 2011.

The prerequisites for this are as follows:
 * Alternatively you can download and import a solution containing these 2 web resources from here.

For all forms where you want to employ the use of this retrieve feature, you will need to load the jquery and JSON resources:


Now go ahead and place the following function into one of your form jscript resources:

function retrieveEntityById(entity,entityid,fields,fn) {

    var context = Xrm.Page.context;
  var serverUrl = context.getServerUrl();
  var oDataSelect;
    // build query string
  oDataSelect = "/XRMServices/2011/OrganizationData.svc/" + entity + "Set(guid'" + entityid + "')" + fields + "";

    $.ajax({
  
        type: "GET",
        contentType: "application/json; charset=utf-8",
        datatype: "json",
        url: serverUrl + oDataSelect,
        beforeSend: function (XMLHttpRequest) { XMLHttpRequest.setRequestHeader("Accept", "application/json"); },
        success: function (data, textStatus, XmlHttpRequest) {
            fn(data.d);
        },
        error: function (xmlHttpRequest, textStatus, errorThrown) {
            alert("Status: " + textStatus + "; ErrorThrown: " + errorThrown);
        }
    });
}

The next step is to call the above function to retrieve the values you are looking for. So for example, if you are on the Quote Product form and you wish to retrieve additional product attributes, you will call the function passing in the following fields:
  • entity - the entity logical name e.g. Product, Contact, Account
  • entityid - the id of the passed in entity
  • fields - you wish returned from the retrieve query. NB: These are case sensitive so be sure to ensure to check how they are defined on the entity. You also can use the OData Query tool to get the syntax. Errors encountered with configuring this are most likely to be in this area.
  • fn - the name of the function you will be using to handle returned results

 function retrieveSample() {

          //Update next 3 lines. NB: Field names in select are case sensitive and must adhere to the schema name
          var entity = "Product";
          var entityid = Xrm.Page.getAttribute("productid").getValue();
          var fields = "?$select=ProductId,DefaultUoMId,ProductNumber";
          
          if (entityid != null) {
            entityData = retrieveEntityById(entity,entityid[0].id,fields,actionFunction);
          }
        }

Finally, you'll need to define the action function to handle the results returned. Below is a sample of how to retrieve the data elements.

 
function actionFunction(entityData) {
                    if (entityData != null) {  
                        Xrm.Page.getAttribute("address1_stateorprovince").setValue(entityData.snt_State);
                        Xrm.Page.getAttribute("address1_city").setValue(entityData.snt_City);
                        Xrm.Page.getAttribute("address1_postalcode").setValue(entityData.snt_name);
                    }    
                }

Now perform whatever jscript manipulation you need to do on the returned results!

Thursday, April 5, 2012

Adding an Advanced Find Query to form

One of the major benefits of CRM 2011 over its predecessor is the ease with which you are able to add sub-grids into a CRM form. This feature has for all intents and purposes replaced the scripting approach to I-Framing in sub-grids that was necessary in 4.0. For the most part that is...

The CRM 2011 feature is very nifty and comes with the following configuration options:

  • All Record Types - This is pretty much the equivalent of a "hard-coded" grid in a form. That is, because the grid brings all records that are returned by a particular view it is static and will render the same grid on all open forms for the given entity.
  • Only Related Records - This uses the view definition but adds an additional clause that personalizes the view to the record being view (via the referential parent/child relationship in the database). This is generally the configuration that you will use in 90% of cases.

There are however situations where the above configuration options will not suffice. This will be in cases where you want to display a sub-grid on a form via a relationship that is not defined by the default parent/child referential relationship. For example, if the relationship is 2 layers deep (i.e. grandparent/child) - in this case although the records are indirectly related, you won't be able to use the default "Only Related Records" option to make these appear on a sub-grid on a form.

You could of course construct such a view using the Advanced Find view. And if we could then I-Frame this view onto the form, we could also incorporate this more complex relationship via a form grid to cover the small percentage of relationship cases that are not catered for by the out of the box configuration options.

And fortunately we can. The following steps illustrate how this can be achieved. This walkthrough builds on work that others have done.

  • Start by building a system view and build the basic filter criteria for the view.


  • Using SQL retrieve the fetchXML for the view (you could also download the fetchXML if building this using Advanced Find)
select FetchXml from SavedQuery where Name = 'Email Contact Grid'

  • Take the fetchXML and format it appropriately into a jscript function (you can use the example below). Note: you should make it dynamic by replacing the hard-coded filter from the Advanced Find query with a dynamic parameter (highlighted below)

function DisplaySubGrid() {

    var subgrid = document.getElementById("Emails");
    if (subgrid == null) {
        //The subgrid hasn't loaded, wait 1 second and then try again
        setTimeout('DisplaySubGrid()', 1000);
        return;
    } 

    var fetchXml = "<fetch>"
      + " <entity name='email'>"
      + " <attribute name='from' />"
      + " <attribute name='to' />"
      + " <attribute name='subject' />"
      + " <attribute name='modifiedon' />"
      + " <attribute name='activityid' />"
      + " <order attribute='modifiedon' descending='true' />"
      + " <link-entity name='activityparty' from='activityid' to='activityid' alias='aa'>"
      + " <filter type='and'><condition attribute='partyid' operator='eq' uiname='xxx'"
      + " uitype='contact' value='" + Xrm.Page.data.entity.getId() + "' />"
      + " </filter>"
      + " </link-entity>"
      + " </entity>"
      + " </fetch>";

    //Inject the new fetchXml
    subgrid.control.setParameter("fetchXml", fetchXml);
    //Force the subgrid to refresh
    subgrid.control.refresh();
}

  • Go back to your system query and clear all the filter criteria you used for obtaining the above and then add filter criteria that will never return any rows (make sure this references an indexed column such as createdon or modifiedon). Also make sure the columns of your view mirror the columns in the FetchXML above. The filter criteria are only dummy as they will be replaced by the dynamic query at run time, but we should be careful to think of potential performance implications assuming the intercept happens after the initial view loads - so be sure it loads no records and is well indexed.

  • Go to your form and insert a sub-grid with the "All Record Types" seleted and the Default View referencing the view that you created above. NB: Make sure the subgrid name in the function matches the name of the form subgrid.

  •  Place the jscript function into the form and call from the form onload event. Also ensure that
  • Now when the form loads, it will pump the query from Advanced Find query into the form grid overriding the definition of that view.

Wednesday, April 4, 2012

Adding drop down menus to the ribbon

Having briefly discussed the Ribbon Workbench Tool I thought I'd go about providing an example of how to go about adding a drop down menu on the CRM entity form.

Rather than keeping it simple, we'll dive straight into the deep-end and walk through an example of configuring a dynamic drop down menu. Chances are that if you are configuring something like this, you'll need to make it dynamic so it can interact with the elements on your form. This example builds on some other contributions that have been made.

We'll start off with the end result. Say we want to configure the ability to directly call a contact using either the business, mobile or home number present on the contact form. This solution should be dynamic in that it needs to pull the relevant numbers from the form and display them in a drop menu. Should one of the numbers not be present, then the corresponding menu option should not show.

Something like this:


The Ribbon Workbench Tool is a good starting point as it allows you to build the initial framework. We'll skip over that part and go ino the configuration of the ribbon component and the corresponding jscript. We'll do this at a fairly high level - use the screenshot below for reference.



  1. In the FlyoutAnchor node make sure to specify the "PopulateDynamically = true" and create a reference to a command definition in the PopulateQueryCommand tag. This function is going to be responsible for creating the menu options.
  2. Create the corresponding "Snt.DynamicMenu" Command Definition and reference the "DynamicMenu" jscript function (in an existing web resource jscript file). This simply calls the jscript function and passes in the context variable.
  3. The "DynamicMenu" jscript function needs to construct and return the XML for populating the menu options. In this function, you can use all the conditional processing of jscript in order to construct the menu options to suit your implementation.
  4. The Command tag in the body of the jscript XML should reference a valid Command Definition back in the RibbonDiffXml section. I'd recommend sticking to the names that have been used (unless you have more than one custom drop  down menu on your form in which case you'll need to distinguish).
  5. Create the corresponding "Snt.OptionClicked Command Definition and...
  6. reference the "OptionClicked" jscript function. This simply calls the jscript function and passes in the context variable.
  7. The "OptionClicked" jscript function is used to handle the mouse click. You can just use the case statement to identify on which menu option the mouse was clicked and perform the necessary processing logic.
That's pretty much it. You can download the files referenced in the screenshots above from here.

Wednesday, March 28, 2012

Read only URLs cannot be opened

There is this annoying little issue whereby read only URLs on a CRM form cannot be opened. There really seems to be no rhyme or reason for such behavior as a URL by definition takes the user out of the context of the form and therefore I cannot fathom why such an action should be prevented.

The alternative is to make the field editable but that might not be an attractive alternative especially if you do not want this field to be updated!

I came across an interesting solution for this whereby you can keep the field editable but just remove the option of, ...well..., editing the field. To me it just seems like a different way of making a field read only that doesn't suppress the ability to open the link. And that works for me...

To do so, leave the field editable on the form and place the following line in you onload form jscript:

document.all.websiteurl.contentEditable = "false";

Replace "websiteurl" with the URL field you are working. The result is that the field appears editable on the form but if you try and edit, you will not be able to. Double clicking on the field will launch the URL specified.



I do not think this is supported script so use discretion in applying.

Wednesday, December 7, 2011

CRM 2011: Java Script Reference

I found the following article to be quite useful so I thought I would re-post it:

http://gtcrm.wordpress.com/2011/03/16/java-script-referenceupdated-2/

It has lots of useful examples of how to perform various form manipulations in CRM 2011.

Wednesday, November 16, 2011

CRM 2011 Update Rollup 5 form issues

As mentioned previously, CRM 2011 Update Rollup 5 in particular introduces some nice new improvements/enhancements. But also as mentioned this rollup cannot be uninstalled. Or technically it can be uninstalled by essentially uninstalling CRM 2011, restoring a database backup and re-installing CRM 2011 again along with the previous rollups to get back to where you started...

So knowing that this is the case, I came across a perplexing issue which appears to have been introduced by Update Rollup 5 that caused a form to no longer render correctly. The end result was that I was receiving the fairly generic "Error on page" message and as indicated in a former post on the topic, the error seemed to occur even when jscript was disabled. And so - using the same process of elimination technique described in that post - I went about removing snippets of jscript until the root cause of the issue was discovered.

The issue came down to the application ribbon EnableRules in the particular case where the EnableRule references a custom function within the form jscript (as described here).This didn't really seem to make any sense but it was evidently the case as every time the function was removed, the form error went away and vice versa. Delving a little deeper it was determined that if the function was left in but one of the form global variables that was referenced was commented out, the form would once again render properly.

So the conclusion that was reached was as follows:

  • Before rollup 5 it appears the form onload script was executed prior to executing the custom EnableRules functions defined in the application ribbon. This allowed for global form variables to be declared that a custom EnableRule jscript function could then reference.
  • Post rollup 5 it appears that the load order was altered such that the form onload script no longer executes prior to the execution of application ribbon EnableRules functions. This means that any global variables referenced in a custom EnableRule jscript function are undefined and will result in the form error described above.

That's at least what I deduced from the behavior that I observed. This issue was reported to Microsoft but rather than waiting for a resolution (which could take a while) or trying to painstakingly revert to a previous rollup, I implemented a workaround solution which was to essentially do away with the form global variable and rather recalculate the value in the EnableRule custom function itself. Once implemented in this way, the form was happy again.

Needless to say the above issue was perplexing indeed - you know the feeling - where you start second guessing your own sanity... So if you encounter such behavior after installing the above rollup, you may want to look into this as one of the potential reasons for the strange form behavior.