Sticky

This blog has moved to www.dreamingincrm.com. Please update your feed Url. Thank you.

28 January 2015

Bookmarklet to copy FetchXml

This is basically an extension of this post -> http://nycrmdev.blogspot.com.au/2014/12/using-advanced-find-fetchxml-capability.html. The bookmarklet below displays fetchxml that was used to display the results.

javascript:var fetchXml;if(frames[0]&&frames[0].document.getElementById('FetchXml')){fetchXml=frames[0].document.getElementById('FetchXml').value;} else if(frames[1]&&frames[1].document.getElementById('FetchXml')){fetchXml=frames[1].document.getElementById('FetchXml').value;} if(fetchXml){window.prompt('FetchXml',fetchXml);} else{alert('No FetchXml query found');} void 0;

Run this bookmarket from the Advanced Find results page and it will display the FetchXml in a dialog window for copy pasting.



Sometimes I just want to develop a starting point fetchxml using advanced find and work on it more using FetchXml Builder or Fetch Tester 3000. This bookmarklet saves me the hassle of exporting the fetchxml file, and copy pasting the result into the appropriate tool.

27 January 2015

Notes Control in CRM 2013 SP1

If you have a note control on a form and the default tab on this note control is Notes, no note records might be displayed, even though there are notes that are related to the record. Once you click the Notes link on the form, you'll suddenly see notes being populated. This is a bug that has been fixed in 6.1.1.


In order to automatically click the Notes link, I have a small script running in the onLoad event of the form to fix this issue on CRM 2013 SP1. This script is a stop gap measure, till 6.1.1 is applied to the server.

setTimeout("(function() { var t = document.querySelector('.tabLink.active'); if(t) { t.click(); } })();",2000);

This issue doesn't seem to be happening in CRM2013 SP1 UR1 and CRM2015 and hence this (unsupported) fix is not applicable to these versions.

GetAttributeValue and null DateTime

Previously, I used to access the value of a property like entity["attributename"] and cast the result into the appropriate type. The preceding line to this, would always be a check to see if the property exists, as it can cause a KeyNotFoundException, without this check. GetAttributeValue is how I access attribute values these days. These are some influential posts that made me change my behaviour.

David Berry -> http://crmentropy.blogspot.com.au/2013/08/entitygetattributevalue-explained.html
Guido Preite -> http://www.crmanswers.net/2014/09/getattributevalue-activityparty.html

While this does prevent KeyNotFoundException, it is important to understand the behaviour of GetAttributeValue, w.r.t DateTime. When GetAttributeValue is invoked to retrieve a DateTime attribute, and the value of the attribute is null, it returns a DateTime.MinValue, which is 01/01/0001.

In a scenario where a retrieved value is used to update another record, you'll have to check if this is DateTime.MinValue before updating, or it will cause an exception like the one below.

The exception thrown is "DateTime is less than minumum[sic] value supported by CrmDateTime. Actual value: 01/01/0001 11:00:00, Minimum value supported: 01/01/1900 00:00:00". To prevent this exception, I check if the retrieved DateTime value == DateTime.MinValue, and if so, choose not to update the target property, or set it as null, depending on the requirement. It is also a realisation for me, that CrmDateTime still lives on, somewhere in the Sdk assemblies.

EDIT (29/01/15): Following David's tip from the comment below, the better approach is to use nullable types with GetAttributeValue, so I should be using DateTime? instead of DateTime.

21 January 2015

Quick Tip: Don't use underscore in Action argument name

There seems to be a bug in the process editor, when you use it to define an action that contains an argument with an underscore in the name. Once you save this action, which meets this criteria, you will not be able to open the action definition again through the process editor. You just get a generic error dialog when you try to open the action.


My action definition itself is minimal. It doesn't contain anything other than the argument (screenshot after following recovery steps).


The underlying error that is found in the url is:

Error code: 0x80040216
Error description: Invalid variable name format

In order to recover from the error follow these steps

1.) Create a new solution and add the action to this solution
2.) Export the solution as an unmanaged solution
3.) Unzip the solution to a location
4.) In the workflow folder, you will find a xaml file. Open this using any text editor
5.) Find and replace the parameter name which has the underscore, to be without underscore
6.) Rezip and import

The action can be opened again after following these steps. I tested this issue and can confirm that it  happens in CRM 2013 6.1.1 and CRM Online.

19 January 2015

Business Rules by Form Type

Xrm.Page.ui.getFormType() is used in form script to find out what type of form is currently loaded. Sometimes, we want to apply a certain logic, depending on whether it is a create form or update form. e.g I want to disable some fields, if it is an update form. If we are using Business Rules, it is not very obvious (at least to me) on how this can be achieved. The answer is quite simple: just check the value of any of these system fields (created, createdon, modifiedby, modifiedon). Here is a business rule that will trigger only for update form.

Here is the rule for create form.


Here is the result after the rule has run on an existing record


The important thing to remember is: The system field you are checking (in this case createdon), has to be on the form. Otherwise the rule will not fire.

Credits to @BernadoNH for this info.

15 January 2015

An alternative approach to loading form scripts in Dynamics CRM

With each browser update, full ES6 support has been getting closer and closer. But this is still sometime away, and transpilers like traceur or 6to5 can help bridge the gap in some areas. One ES6 functionality I am very much interested in, is module. As of today, no browser natively supports this, and I would have to transpile my code to get this functionality.

So, I once again would like to use my favorite module loader, requirejs for doing this. The last time I did this (http://nycrmdev.blogspot.com.au/2014/04/using-requirejs-in-crm2013.html) I had to use some unsupported tricks to get this working in CRM2013. This time, my approach is to do away with CRM script loading mechanism altogether and use requirejs to load the form scripts.

Here is how my resources are organised.

 Events.html is the HTML webresource that will be embedded in the entity form. Here is the code for events.html;

<!DOCTYPE html>
<html>
<head>
  <title>Form Events</title>
  <!-- data-main attribute tells require.js to load
  scripts/main.js after require.js loads. -->
  <script data-main="scripts/main" src="scripts/require.js"></script>
</head>
<body>
  <ul id="events">
  </ul>
</body>
</html>

main.js is the entry point into the form processing code.

(function () {
  var defaultConfig = {
    shim: {
      'lodash': {
        exports: '_'
      }
    },
    deps: ['lodash', 'common', 'ryr_eventform'],
    callback: function() {
      console.log('callback before requirejs has been loaded');
    },
    onError: function(err) {
      console.log(err.requireType);
      if (err.requireType === 'timeout') {
        console.log('modules: ' + err.requireModules);
      }
      throw err;
    }
  };
  defaultConfig.callback = function() {
    console.log('callback after requirejs has been loaded');
  };
  requirejs.config(defaultConfig);
})(); 

I want the scripts to load in the following order: lodash->common->ryr_eventform. If you use the form area to reference your script, you really don't have any control over the sequence, as they are loaded async and may not be loaded in the same order you added them in the form (http://www.develop1.net/public/post/Asynchronous-loading-of-JavaScript-Web-Resources-after-U12POLARIS.aspx). Until Microsoft changes this functionality, there are two ways to overcome this issue.

1.) Bundle all your scripts in the order of their dependencies
2.) Check if the dependency has loaded. (See the waitForScript technique in the develop1 link)

I am loading the scripts using requirejs, but the triggering page is an external web resource. This way I can keep this a supported method.

This is common.js, which is required by ryr_eventform.js.

define(['lodash'], function (_) {
    var common = {
      log: function(message) {
        var e = document.createElement("li");
        e.innerHTML = new Date().toString().split(' ')
        .filter(function(d,i){ return i>0 && i<=4})
        .join(' ') +': '+ message;
        document.getElementById('events').appendChild(e);
      }
    };
    common.log('Loading common_script.js');
    //can use lodash, as it is specified as a dependency and should have been loaded
    common.log('Lodash Version: '+_.VERSION);
    return common;
});

This is ryr_eventform.js.

define(['common', 'lodash'], function (common,_) {
 common.log('Loading ryr_eventform.js');
 //can use lodash, as it is specified as a dependency and should have been loaded
 common.log('Lodash Version: '+_.VERSION);
 var Xrm = parent.Xrm;

 var form = {
  onSave: function(context) {
   common.log('Form Save Event');
  },
  onLoad: function() {
   common.log('Form Load Event');
   if(Xrm){
    Xrm.Page.data.entity.addOnSave(this.onSave);
    Xrm.Page.getAttribute('ryr_name').addOnChange(function(context) {
     common.log('Name Change Event: ' + context.getEventSource().getValue());
    });
    }
   else{
    common.log('Web Resource has not been embedded inside a CRM form');
   }
  }
 };

 form.onLoad();
 return form;
});

Here is how the form looks in the design mode.

I have not added any scripts to the form.



Since requirejs will start loading the scripts, you don't need to worry about this.

Lets start looking at some form events now and how the script behaves.

Form Load

Name field changed
 As you, can see a script can do exactly the same things, even though it has not been been loaded through the CRM form script loading mechanism. These are are two key things that help to achieve this.

1.) The webresource folder structure
2.) Referencing Xrm object from webresource using parent.Xrm

The impetus for this post is this: I have got "The form has changed. Would you like to save your changes" dialog more than a few times and I have no idea what is the reason for this dialog. If the change has made by a script, I have no way of knowing what the change was, and which script triggered this, unless I have added some console.log message the scripts. This is not possible if I can't change the script. You could live edit the script using the DevTools, but I don't want to do that.

The disadvantages of this techique, that I can see are
  1. html webresource has to be added to the form
  2. Tablet support
If CRM Client API exposes some sort of event listening capability, this would help the devs to listen to certain events like form save, form load, field onchange from the devtools console and figure out what is happening with the form, without using the debugger step through. 

CRM itself, uses custom events and listeners internally, to figure out what scripts to execute for a particular event. But this functionality is not exposed externally for everyone to use. Until this is made available down the line, into some sort of Client API - Dev Mode, I can use this to control the form script loading process and audit of form events.