Automatically executing HTTP POST requests in Dynamics 365 iframes - part 2

Several months ago, I wrote a post that showed how to automatically display the results of an HTTP POST request inside a Dynamics 365 iframe. I was working on a project last week where I was tried to use that approach, but I ran into some problems, so today I will present an updated approach.

Specifically, my iframe needed to post a key value that was retrieved asynchronously by a function that was called when the main CRM form loaded. Because the iframe was contained in a collapsed tab, the iframe might not be loaded by the time the main form's script had retrieved the key value, so trying to dynamically write out HTML to the iframe wouldn't work like I showed previously. I tried a few different ways to trigger the "write a dynamic form and submit it" code after retrieving the key value, but I never could get it working consistently with an iframe in a collapsed tab.

The solution I found was to create a new helper HTML web resource and then load that into the iframe when the main form loads. The helper page then used an onload event to check to see if the main form's script had retrieved the key value for posting yet. Once the key value was available, then the helper page used it to execute the form submission. The passing of the key value from the main form to the helper page was handled with the JavaScript postMessage function. Here's what that flow looked like:
Iframe posting flow

Here's the code I am running in the main CRM form:

var _keyvalue = '';

var form_OnLoad = function (){
	//add a listener for the message from the iframe
	window.addEventListener('message', receiveMessage, false);
	Xrm.Page.ui.controls.get('NAME_OF_YOUR_IFRAME').setSrc('PATH_TO_YOUR_HELPER_RESOURCE');

	//run some other code that sets the value of _keyvalue asynchronously 
	//...
	//...
}    

var receiveMessage = function (event){
	//make sure that we are only responding to messages from the same origin as the CRM form
	var originarr = Xrm.Page.context.getClientUrl().split('/');
	originarr.pop();
	var origin = originarr.join('/');
	
	//if not, return
	if (event.origin !== origin)
		return;
	
	//if the received message is "getkeyvalue," send a response
	if(event.data == 'getkeyvalue'){
		event.source.postMessage(_keyvalue, event.origin);
	}
}

Here is the code for the helper page:

<html>
<head>
	<title>helper page</title>
	<script src="ClientGlobalContext.js.aspx" type="text/javascript"></script>
	<script src="https://code.jquery.com/jquery-2.2.4.min.js" type="text/javascript"></script>
	<script>
	//default the keyvalue to an empty string
	var _keyvalue = '';

	var receiveMessage = function (event){
		//make sure that we are only responding to messages from the same origin as the CRM form
		var originarr = Xrm.Page.context.getClientUrl().split('/');
		originarr.pop();
		var origin = originarr.join('/');
		if (event.origin !== origin)
			return;
		
		if(event.data){
			_keyvalue = event.data;
		}
	}

	//start checking for the keyvalue from the parent form when the document is ready
	$(function(){
		//register the message listener
		window.addEventListener("message", receiveMessage, false);
		var checkinterval = null;
		var waittime = 100; //check every X ms
		checkinterval = setInterval(
			function(){
				//ask the parent for the keyvalue
				var originarr = Xrm.Page.context.getClientUrl().split('/');
				originarr.pop();
				var origin = originarr.join('/');
				parent.postMessage('getkeyvalue', origin);
				
				//if parent form has responded with a non-empty keyvalue
				if(_keyvalue!=''){
					//stop the cycle
					clearInterval(checkinterval);
					
					//set the value of the form input
					$('#keyvalue').attr('value',_keyvalue);

					//submit the form
					$('form').submit();
				}
			}, waittime
		);
	});
	</script>
</head>
<body>
	Loading . . .
	<form id="webpartform" method="POST" action="SOME_DESTINATION">
		<input type="hidden" name="keyvalue" id="keyvalue" value="" />
	</form>
</body>
</html>

Two additional points:

  1. My implementation is actually a bit more sophisticated than what I've shared here. Instead of just passing a string key value, I pass a JSON object that contains the key value and the correct URL for the form action so that I can use the same helper page for multiple targets. Once you get the postMessage calls working between a parent CRM form and its iframe(s), you can handle many different complex scenarios.
  2. I suspect this approach is on right on the edge of the supported/unsupported line. The SDK documentation for using JavaScript does say not to access the Document Object Model (DOM). Technically this approach uses the Browser Object Model (BOM), so it's not explicitly unsupported, but I could see changes to Dynamics 365 impacting it in the future, so that's just something to keep in mind.
comments powered by Disqus