Exchanging data between CRM Forms and IFRAMEs

One of the most common requirements when we add content in IFRAMEs or Web Resourses in CRM is to have the ability to communicate with the calling or source CRM form to perform all sorts of operation. This article explains how this can be achieved using the postMessage JavaScript messaging mechanism.

What does postMessage do?

It’s a JavaScript method which was initially created to facilitate cross-origin communication between web pages. There are often valid scenario in which we need to display a page from another website in a new window or in an IFRAME. That’s the easy part. The more complicated part is when there is a requirement to perform some operations on the source page based on a action that occurs on the external page in the new window or IFRAME.

In the example below, consider we have a contact form that displays an external page in an IFRAME. When a button in clicked in the IFRAME, information (example, a lookup value) is passed to the CRM form, the value is set on the screen and the form is saved.

Step 1 – Write methods for the CRM Form: Register Listener, Message Handler  

The code below contains two methods, the first one (RegisterListener) sets a method that will be called if the postMessage is invoked (UpdateContactLookup). 


SADAX.Contact =
{
   RegisterListener: function () {

     if (window.addEventListener) {
       window.addEventListener('message', SADAX.Contact.UpdateContactLookup, false);
     }
     else { // IE8 or earlier
       window.attachEvent('onmessage', SADAX.Contact.UpdateContactLookup);
     }
   },

   UpdateContactLookup: function (event) {

     var origin = "";
     if (event.domain) origin = event.domain; // IE
     else if (event.origin) origin = event.origin; // FireFox - Chrome

     if ("https://source" == origin) {

       if (event.data != null) {

         var entity = SadaxJSON.parse(event.data);
         if (entity) {

           var value = new Array();
           value[0] = new Object();
           value[0].id = entity.Id;
           value[0].entityType = entity.LogicalName;

           var lookup = Xrm.Page.getAttribute("sadax_referencecontactid");
           lookup.setValue(value);
Xrm.Page.data.save();
}
}
else
{
alert("This message has been posted by an unknown source ('" + origin + "', expected 'https://source').");
return;
}
} 
}

Step 2 – Register the listener on Form’s OnLoad event

image

Step 3 – Write Code for the IFRAME to post the Message from the IFRAME

After the business logic execution, the postMessage method is called on the parent CRM form as follows:

function SetContactLookup(entityReference)
{
   var entity = {};
   entity.Id = entityReference.id;
   entity.LogicalName = entityReference.entityType;

   window.parent.postMessage(SadaxJSON.stringify(entity), "https://source");
}

Comments / Wrap up

At step 3, using the postMessage on the window.parent will cause the SADAX.Contact.UpdateContactLookup method to be fired on the parent CRM form. Notice that the method receives an event object as a parameter. The content of the object slightly differs based on the browser being used (IE vs rest of the world). Mostly, you should pay attention to the event.domain or event.origin attributes. This is used to validate the website that posted the message is safe (i.e. the one you are expecting).

There is also an event.data attribute that contains parameters that are sent from one page to another. In this case, we are using a renamed JSON library to stringify our custom object types. Passing an object without stringifying it would work, but we found it didn’t work well in all browsers/version. The reason for the renamed JSON library is again browser compatibility reasons. If IE8 is out of scope for you, you probably do not need this.

Notes:

  • This also work for web resources opened in a different window using window.open or Xrm.Utility.openWebResource
  • IE8 doesn’t allow to postMessage to other windows, only to iframes.
  • I haven’t tried this in CRM Online so you can try it by yourself if need be.

Dynamics CRM – What Options for Editable Grids?

I have yet to be on a Dynamics CRM project where no one mentions or asks about the possibility to edit information directly from a grid or a sub-grid. In the interest of saving time when I’m recommending solutions to my clients, I thought I’d build a grid to gather the options and additional information when available.

Product Name & URL Supported CRM Versions Cost Comment(s) and Links
Sparkle XRM Framework CRM 2011 CRM 2013

CRM 2015

CRM Online

Open Source Library OK. It’s not fair to call this an Editable Grid per say. Sparkle XRM is an Open Source development library to help build complex visual components for Dynamics CRM. It is initiative from MVP Scott Durow. With strong developer, you can leverage Sparkle to build your own editable grid. See how it can be done here.
AbleBridge EditAble Grid CRM Online CRM 2015

CRM 2013

Not public Out of the few products that I have tested and seen in action, it is easily the best one out there. It is also the most expensive. I guess it’s true when they say you get what you pay for. You can read my product review from last year. Editable Grid official overview
PowerObject Power Grid CRM Online CRM 2015

CRM 2013

1$ per CRM user per month MSDynamicsWorld article on PowerGrid.
Sonoma Partners Editable Grid for CRM 2013 CRM 2013 Free Anything that’s free, I happy about! That being said, it is a bit tricky to configure when you are not a bit technical. See configuration here.
MTC’s Super Grid CRM 2011 CRM 2013

CRM 2015

CRM Online

Org License : $1000 + User License : $15 per user Last I used this (about a 1.5 year ago). MTC was transitioning away from its Silverlight controls and implementing an HTML/JavaScript based editable Grid. It wasn’t perfect, but it did the job. It works by adding an HTML5 graphical UI “overlay”, to CRM standard or custom Views. What bothers me is that there is not an advanced view configurator. It does come with an API to perform these advance operations such as color coding rows/cells, hide columns, make fields required etc., so that is a good consolation prize.
BusiPlanIT’s free customizable editable Grid CRM 2013 CRM 2015

CRM Online

One grid for free – Pay for more than one. This is a great free add-on. I would pay for it. If your needs are simple and you just need to edit records inline for one record time, go for it. You need to contact BusiPlanIT for pricing information if you need more than one grid.

Obviously, there is always the possibility to build an editable grid yourself using JavaScript libraries of your choice. It’s just a matter of how much time you need and want to invest versus creating a dependency to an add-on. Eternal debate.

I thought I should also mention the availability of the Immersive Excel experience with CRM Online (2015 Update 1). Though it can be used to perform inline editing on list views, it is really meant to to do quick analysis right from CRM Online. There is plenty of articles on the subject out there. Check out Inogic’s article here.

Hope this helps!

 

CRM 2013 – Client API New Functionalities Recap

If you’ve been following, I had a series of Client API posts late last year. Below I have regrouped the new Client API functionality in a table for reference. This can become helpful especially if you are developing with the “old” CRM 2011 mindset to easily see what you can achieve differently and/or more efficiently in CRM 2013. Details on the new functionalities is available on the Microsoft Dynamics CRM YouTube video and in the CRM 2013 SDK.

Area

Method and Syntax

Description

Data

Xrm.Page.data.refresh(save).then(successCallback, errorCallback)

Asynchronously refreshes and optionally saves all the data of the form without reloading the page.

You can pass a callback method to execute on success or error

Data

Xrm.Page.data.save().then(successCallback, errorCallback)

See my blog post on this here

Data

Xrm.Page.data.getIsValid()

Returns a Boolean value indicating if the form can be saved or not

Data

Xrm.Page.data.setFormDirty()

Set the form as dirty

Entity

Xrm.Page.data.entity.getPrimaryAttributeValue()

Gets a string for the value of the primary attribute of the entity.

UI

Xrm.Page.ui.setFormNotification(message, level, uniqueId);

Xrm.Page.ui.clearFormNotification(uniqueId)

Use setFormNotification to display form level notifications and clearFormNotification to remove notifications. Gareth Tucker has a great article on these here.

Controls

Xrm.Page.getControl(fieldName).setNotification(message)

Xrm.Page.getControl(fieldName).clearNotification()

See article linked above for details. Notifications can be set at the field level

Number

Xrm.Page.getAttribute(fieldName).setPrecision(precision)

This method is used to override the field’s precision

Date

Xrm.Page.getControl(arg).setShowTime(bool)

Specify whether a date control should show the time portion of the date.

Date

Xrm.Page.getControl(arg).setIsAllDay(bool)

Specify whether a date control should set a value including the entire day.

Lookup

Xrm.Page.getControl(arg).addCustomFilter(filter, entityLogicaName)

Use add additional filters to the results displayed in the lookup. Each filter will be combined with any previously added filters as an ‘AND’ condition

The entity logical name is optional. If this is set the filter will only apply to that entity type. Otherwise it will apply to all types of entities returned.

Lookup

Xrm.Page.getControl(arg).addPreSearch(handler)

Use this method to apply changes to lookups based on values current just as the user is about to view results for the lookup.

The argument is a function that will be run just before the search to provide results for a lookup occurs. You can use this handler to call one of the other lookup control functions and improve the results to be displayed in the lookup.

Lookup

Xrm.Page.getControl(arg).removePreSearch(handler)

This API call is used to remove event handler functions that have previously been set for the PreSearch event (see above)

Context

Xrm.Page.context.getUserName()

Returns the name of the current user.

Context

Xrm.Page.context.client.getClient()

Xrm.Page.context.client.getClientState()

See my blog post about it here

Utility

Xrm.Utility.alertDialog(message,onCloseCallback)

Xrm.Utility.confirmDialog(message,yesCloseCallback,noCloseCallback)

Displays a dialog box containing an application-defined message.

Displays a confirmation dialog box that contains an optional message as well as OK and Cancel buttons.

See additional notes on this here.

Utility

Xrm.Utility.isActivityType(entityName)

Determine if an entity is an activity entity.

Utility

Xrm.Utility.openEntityForm(name,id,parameters)

Opens an entity form. Parameters are optional and used to pass the form ID, set default field values or pass custom query string parameters

Utility

Xrm.Utility.openWebResource(webResourceName,webResourceData,width, height)

Opens an HTML web resource.

CRM 2013 – Client API: Save Event Arguments

There are a few useful additions that have been added to the client API around the Save event. They are 3 methods that have been formally introduced:

getSaveMode: Returns a value indicating how the save event was initiated by the user.
isDefaultPrevented: Returns a value indicating whether the save event has been canceled because the preventDefault method was used in this event hander or a previous event handler.
preventDefault: Cancels the save operation, but all remaining handlers for the event will still be executed.

The key method is the getSaveMode function. Think about the new auto-save feature on updated forms. The getSaveMode function allow the javascript method executing on the Save event to know why/how the record is being saved. That gives you the flexibility as a developer to add some additional logic to handle your scenario… Below is the list of values returned by the getSaveMode function based on the entity type.

Entity Event Mode Value
All Save 1
All Save and Close 2
All Save and New 59
All AutoSave 70
Activities Save as Completed 58
All Deactivate 5
All Reactivate 6
User or Team owned entities Assign 47
Email (E-mail) Send 7
Lead Qualify 16
Lead Disqualify 15

This is fantastic as you can now write script to handle very specific scenario like an activity being resolved, a record being saved and closed, assigned and other cases. Below is a usage example in which we prevent the auto-save from happening as presented in the SDK documentation:

function preventAutoSave(econtext) {
   var eventArgs = econtext.getEventArgs();
   if (eventArgs.getSaveMode() == 70) {
      eventArgs.preventDefault();
   }
}

Cheers

CRM 2013 – Client API: Save & Refresh

I know these operations seem like very basic functionalities that have been around for a while. I wanted to point a specific new feature that comes with CRM 2013: the ability to run a callback method after a refresh or after a save triggered from a JavaScript method.

In the past, using the old Xrm.Page.data.entity.save method caused a refresh of the page which dismissed any code after the save in your script. Take this scenario for example…

  • You have button in your ribbon on an Account form
  • The button calls an external web service to update some of the account field values based on the account’s Primary Contact value which is on editable on the form
  • The web service only takes the account id as a parameter, it loads the records, reads it and takes action

With the old save method, you had to require that the user saves the account so that the value of Primary Contact is saved to the database, then click your button that will call the web service with account ID. Such a requirement certainly causes pain in user experience especially if there are a lot of situations where they need to hit save, wait for refresh and click another button.

With the new Save and Refresh, we now have the possibility to add callback methods:

Xrm.Page.data.refresh(save).then(successCallback, errorCallback);

// Parameters
// save => A Boolean value to indicate if data should be saved after it is refreshed.
// successCallback => A function to call when the operation succeed
// errorCallbak => A function to call when the operation fails. It gets called with 2 parameters (an error code and a localized error message)

Xrm.Page.data.save().then(successCallback, errorCallback);

// Parameters
// successCallback => A function to call when the operation succeed
// errorCallbak => A function to call when the operation fails. It gets called with 2 parameters (an error code and a localized error message)

Having had this problem in the past makes this one a good candidate for most exciting new client API feature for me 🙂

Cheers

CRM 2013 – Client API: Dialogs

CRM 2013 introduces a couple of client side dialog methods under the Xrm.Utility library. The goal is to remove the use of window.open and window.confirm and replaces them with alertDialog and confirmDialog and to support these alert messages in CRM for Tablets. Also, the key difference is that these methods will not block code until a user closes them. However, they contain a callback function parameter to indicate what code should be executed depending on the user’s response.

The Xrm.Utility.alertDialog(message,onCloseCallback) method displays a dialog box that contains a message passed as a parameter. Optionally, you can also execute another method (onCloseCallback) when the OK button is clicked.

var message = "Alert Dialog ! ";
 Xrm.Utility.alertDialog(message, onCloseCallback);
 

The Xrm.Utility.confirmDialog(message, yesCloseCallback, noCloseCallback) method displays a confirmation dialog box that contains an optional message and an OK and a Cancel button. A callback method can be set to run after each of the button is clicked.

var message = "Confirmation Dialog ! ";
 Xrm.Utility.confirmDialog(message, null, null);
 

Again in this case, the methods are only available for “Updated Entities”. You can see my previous post to get the list of entities on which this will not work.

Cheers

CRM 2013 – Client API: Client Methods

The CRM 2013 client API adds a few useful methods. Here I am going to focus on the context.Client methods : context.client.getClient() and context.client.getClientState. These methods are used to find out what kind of client is running and to get the information about it being online or offline. This is useful if you have scripts that you want to run when you are in the web application and nowhere else (not in Outlook client, not on Mobile client). As a consequence of this API change, the previous context.IsOutlookOnline and context.IsOutlookClient are now deprecated.

The getClient method returns the value Web (browser), Outlook (Outlook client) or Mobile (mobile app). The getClientState method returns Online or Offline, the latter applying only to the Outlook and Mobile clients. It’s also worth noting that these methods are only available for what Microsoft calls “Updated Entities”. In short, it’s the entities that have an updated interface (most key entities like Account, Case, Contact, basic activities like Phone Call, Tasks fit into that category). It also works on custom entities.

The entities that are not updated still use the “classic forms” and the Client methods are not supported on these. Here is the list of the entities with classic forms:

Address
Article
Article Comment
Bulk Delete Operation
Connection
Discount
Discount List
Document Location
Email Attachment
Follow
Goal
Goal Metric
Import Source File
Invoice Product
Order Product
Price List
Queue Item
Quote Product
Rollup Field
Rollup Query
Saved View
Service
Service Activity
SharePoint Site
Site
Territory
Unit
Unit Group