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.

Custom User Interface in CRM – HTML5/JavaScript or ASP.NET Web application?

Lots of times on CRM projects, we are required to build and display custom screens inside of Dynamics CRM. There are a lot of factors that come into play in the choice how we build these custom user interfaces. It is a very key decision to make because it can sets the standard technologies and tools that you use for custom screens on project (among other things). I thought I’d share some key points to consider in your decision making process to determine what technologies and tools to use.

The need for custom user interface

First, it is important to know and understand when it is required to build custom UI as part of your CRM solution implementation. As a wrote a few years ago, we sometimes find ourselves in situations where we can come up with a complex data model and we feel like the out of the box UI capabilities won’t be user friendly enough to drive user adoption. Situations like that create the need to build more user friendly interfaces to simply make people’s lives easier when they start using the CRM/XRM application. Some examples are

  • Custom calendar view for timekeeping
  • Tree view to display a complex hierarchy
  • Interactive Bing or Google maps views of CRM data
  • Editable grids
  • Complex grids with aggregates, grouping functionalities
  • Integrated views to other systems

And the list goes on. The needs are very different depending on the nature of the enterprise looking to implement a CRM/XRM solution.

Most common technology choices

From experience, I have seen a lot of custom screens built using the following tools/technologies:

  • Silverlight
  • ASP.NET / ASP.NET MVC
  • HTML/HTML5 + JavaScript + CSS

The screens are usually displayed in web resources or in an IFRAMES inside CRM on forms or as standalone modules (open from a button, display in sitemap area). I will not cover Silverlight since Microsoft announced its end of life and continued support for a few additional years, plus the world has started to move on.

ASP.NET / ASP.NET MVC

  • Pros
    • Flexible server side business logic
    • Easily integration with external systems (web services, databases etc.)
    • Can be very quick to develop depending on the requirements, expertise and accelerators (advanced controls provided by Telerik, Infragistics or others can speed up development time significantly)
    • Develop with a familiar programming language (C# + Visual Studio) – this of course depends on your development background
  • Cons
    • Requires the deployment and maintenance of a hosted web application
    • You have to deal with the weight of ASP.NET (ViewState, Postbacks etc.)
    • Not a great fit with CRM Online and cloud offerings in general (cloud deployment, cross origin web sites, integrations etc)
    • Required additional configuration in CRM + JavaScript to set the IFRAME’s sources (URL)
    • Effort for developing custom web applications is often under-evaluated

HMTL(5)/JavaScript

  • Pros
    • Usually, a much lighter solution and if done properly, custom UI components can be very responsive and provide great user experience
    • All components are in CRM Web Resources (html pages, JavaScript files, images…)
    • Custom screens are CRM Solution aware and there is no need for deploying and maintaining an additional web site on a web server
    • A definite plus for cloud based Dynamics CRM implementations
    • Can be very quick to develop depending on the requirements, expertise and accelerators (Kendo UI controls or others can speed up development time significantly; other frameworks like Knockout JS, Bootstrap can also be useful)
    • Leverage trending technologies and tools
    • Use any IDE you want (no obligation to use Visual Studio, even if you should J)
  • Cons
    • Possibly additional complexity if you need to display information from an external system
    • If not structured properly, JavaScript code can become massive and hard to navigate/maintain
    • For non-web developers, might be a steep learning curve
    • Need to establish good project structure and standards (e.g. use TypeScript or Script Sharp)

What else to consider?

There are other factors to take into account, some being specific to each organization. A few examples:

  • What is the expertise or your resource pool?
  • Do we have people to build and maintain what we deploy?
  • Are we in CRM Online or planning to move to CRM online?
  • Are we, as a company, trying to get into HTML/JavaScript more?

This is good food for thoughts for starters. If you want more guidance, feel free to reach out to us.

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