We (developers) have a great feature in CRM 2011 release. If you want to give your CRM application new client side features or controls not available OOB and you want your solution to remain supported, you have to use HTML/JS webresources. This article describes some approaches, tricks and code snippets I found or developed while using HTML/JS webresources.
Embedding and passing parameters
To pass context of execution (entity and identifier or a record) or some custom parameters, you first have to define it in webresource configuration:
To parse and use the parameters in your webresource, you can use the following code:
var parameters = GetGlobalContext().getQueryStringParameters();
Important Note: To make GetGlobalContext method available, you have to reference ClientGlobalContext.js.aspx in your code:
<script type="text/javascript" src="../ClientGlobalContext.js.aspx"></script>
And here is one way passed parameters could be used:
To get the identifier or type of a record you can use the following code:
var recordid = parameters.id; var entityname = parameters.typename;
Obviously, if parameters.id equals null that means that user creates a record or you haven’t checked the ‘Pass record object-type code and unique identifier as parameters’ checkbox. So if you are sure that you have checked the checkbox and that the id is null, the webresource wil be executed in a context of create form.
If you added a custom parameter or parameters, you can access it using the following code:
var customparams = parameters.data;
If you use multiple parameters and you use the format “Parameter1=Value1&Parameter2=Value2”, the following code could help you in the parsing of parameters passed:
function ParseData(query) { var result = {}; if (typeof query == "undefined" || query == null) { return result; } var queryparts = query.split("&"); for (var i = 0; i < queryparts.length; i++) { var params = queryparts[i].split("="); result[params[0]] = params.length > 1 ? params[1] : null; } return result; }
and here is usage of mentioned method:
var customparams = ParseData(parameters.data); var customparam1 = customparams.CustomParam1; var customparam2 = customparams.CustomParam2;
UPD: Today, I got a brilliant suggestion from my friend and MVP Artem Grunin – try to use JSON format when you pass custom parameters. I tried it and here are the results I got:
Registration of custom parameters:
And usage in webresource code:
As you can see – that work perfectly!
Communication with caller window
If you need to communicate with caller window, you can use following code to get the “Xrm” object:
var Xrm = window.parent.Xrm;
and then access everything normally available with JavaScript code for the CRM form. Below is an example of accessing the field value:
var windownamefieldvalue = Xrm.Page.getAttribute("new_name").getValue();
Subscription to fields and form events
Sometimes you need to react on some events raised in CRM form like OnChange of field or OnSave of form. You can do it using the following code for OnChange:
var Xrm = window.parent.Xrm; Xrm.Page.getAttribute("new_name").addOnChange(function (context) { alert("Field Was Changed"); });
The “context” parameter of a method can be used to get information about call context. Here is a short piece of code that demonstrates how the parameter can be used:
var Xrm = window.parent.Xrm; Xrm.Page.getAttribute("new_name").addOnChange(function (context) { var originAttribute = context.getEventSource();//Attribute that was changed on a form. var callDepth = context.getDepth();//get depth of a call. alert("Attribute " + originAttribute.getName() + " was changed and depth of a call is " + callDepth); });
If you need to handle OnSave event, you can use the following code:
var Xrm = window.parent.Xrm; Xrm.Page.data.entity.addOnSave(function (context) { alert("Form was saved"); });
The “context” parameter is a standard execution context for the Save event used in standard CRM forms. You can use it to analyze Save mode or to cancel Save:
var Xrm = window.parent.Xrm; Xrm.Page.data.entity.addOnSave(function (context) { alert("Form was saved and Save mode is - " + context.getEventArgs().getSaveMode()); context.getEventArgs().preventDefault(); alert("Save was cancelled"); });
Tricks
Fixing the behavior of webresources for which the execution of logic for Create and Update forms differs (special thanks to Inogic for the idea, origin – http://inogic.com/blog/2014/08/update-html-on-form-save-button/)
Let’s assume that you have developed webresources for which the execution logic is different for the Create and Update form. You have opened a new form and, in this case, webresources worked properly (followed ‘Create Form’ logic). You clicked Save and expected that webresource would be reloaded and ‘Update Form’ logic would work. Unfortunately not. CRM doesn’t reload webresources in such scenarios. Add the following code to CRM Form Onload handler to help CRM reload webresource:
var wrid = "Put your webresource control here"; var currenturl = Xrm.Page.getControl(wrid).getSrc(); if (currenturl.toLowerCase() == "/_static/blank.htm") { currenturl = Xrm.Page.getControl(wrid).getInitialUrl(); } if (currenturl.toLowerCase().indexOf("id=", currenturl.length() - 3) != -1) { currenturl += Xrm.Page.data.entity.getId(); Xrm.Page.getControl(wrid).setSrc(currenturl); }
Restore of standard events (special thanks to Spectr, origin – http://axforum.info/forums/showthread.php?p=301306#post301306)
If you have referenced ClientGlobalContext.js.aspx you should be aware that this would cancel the onselectstart, contextmenu, and ondragstart events. If you want to restore those handlers, you can use following code during the onload of webresources:
if (document && document._events && document._events.unload && document._events.unload[0] && document._events.unload[0].handler) { document._events.unload[0].handler(); }
1. Try to use supported approaches. Otherwise, your code could stop working after installing the Updates
2. If you want to build complex and beautiful extensions, start learning (if you haven’t yet done it) JavaScript Frameworks. Here is what I use at the moment:
- jQuery (for easier work with DOM)
- Knockout.js (for complex binding scenarios)
Hello Andrey! Thanks for the article.
But I have some questions:
1. So you’ve sent two parameters with the static values like “CustomParam1 = Value1”, but if I need to pas params dynamically ? Can you describe this method?
2. What if I need to pass a rich text variable like XML content or Base64 string, how can I pass data in this case ?
Thanks again for your work, and thanks in advance for answer on my question 🙂
Hello Jerzy,
When you have massive data to be transferred between form and webresource (that is technically IFrame embedded into your form) the best way to transfer information is to use post messages. Check following articles that describe that approach:
http://crmmemories.blogspot.com/2017/05/post-data-from-dynamics-crm-form-to.html
https://salimadamoncrm.com/2016/03/28/exchanging-data-between-crm-forms-and-iframes/
Thanks,
Andrew
Thank you, Andrew. I’ll try it!
Regards!
Hello Andrew, Thanks for the article. I have following question with regards to Dynamics 365 :
How do we replace window.parent.Xrm.Page.getAttribute that is written in js file.
Thanks
Manish,
ATM there is no replacement for it. Check following post – https://docs.microsoft.com/en-us/dynamics365/get-started/whats-new/customer-engagement/important-changes-coming#some-client-apis-are-deprecated specifically “Comments” column for “Xrm.Page” line, quoting – Although Xrm.Page is deprecated, parent.Xrm.Page will continue to work in case of HTML web resources embedded in forms as this is the only way to access the form context from the HTML web resource. so there is no replacement available at the moment so you you can use it until further notice.
Thanks,
Andrew
This is still a useful post even after all this time. I did have one question though were you mention we can access the query string parameters in the web resource using (var parameters = GetGlobalContext().getQueryStringParameters(). I am a little confused here because according to the MS deprecation page this is still valid and not yet deprecated. However I am calling this method using latest D365 online version and the object returned only contains the id property none of the other ones (typename, type, orgname, userLcid, orgLcid, data, formid, entrypoint). Therefore, my current thinking is either I am doing something wrong or Microsoft *have changed* the API ahead of it actually being deprecated? Appreciate any light you can shed on this one by firing up Chrome on an Online instance and taking a look? 🙂
Antony,
UCI and Classic Web Interface behave differently. Check this post – https://butenko.pro/2018/12/07/html-webresource-classic-ui-vs-uci-story-of-broken-functionality/
Thanks,
Andrew
Thanks I read that post too but I should have mentioned in my original comments that *UCI is disabled in my instance* and I am using the classic web interface (at least according to system settings) so unable to get the object properties as I had expected under the classic interface. Am I missing something more? Thanks
The post was written 10 months ago and there is a chance that Microsoft changed something in the api. If you want to get the stable result – my recommendation is usage of manual parsing of webresource url. You can find the code at the end of this post – https://butenko.pro/2018/12/07/html-webresource-classic-ui-vs-uci-story-of-broken-functionality/
Andrew
Thanks again Andrew, yeah I already had a working solution using the manual approach it was more me trying to understand why Microsoft would change something so fundamental to object model and not give any date or notice on deprecation page? It seems to me that this would break many online implementations so I had assumed maybe it was my mistake somehow but looking at the object returned from getQueryStringParameters() I dont have any other explanation. Thanks again for great blog! I;ll dig deeper on Dynamics Community page.
You are welcome!