Here are ways that were used by developers to show Modal Dialogs:
- Xrm.Internal.openDialog
- Alert.js
- Custom Dialogs that used different frameworks like jQuery UI Dialog or similar
But the main and common issue of options available was that those approaches can’t be considered as supported.
And it stayed the same for years until Microsoft introduced Xrm.Navigation.navigateTo method.
So what’s new here?
Here is the code that can be used to open a modal dialog:
var sessionId = new Date().getTime() + ""; var data = { customParameter: "Custom Parameter Value", sessionId: sessionId }; //custom parameter that you need in the modal dialog var dialogParameters = { pageType: "webresource",//required webresourceName: "ab_/ModalDialog.html",//Html Webresource that will be shown data: data//optional }; var navigationOptions = { target: 2,//use 1 if you want to open page inline or 2 to open it as dialog width: 400, height: 300, position: 1//1 to locate dialog in center and 2 to locate it on the side, title: "My super duper modal dialog!" }; Xrm.Navigation.navigateTo(dialogParameters, navigationOptions).then( function (returnValue) { //returnValue is blank when "Cancel" button is clicked if (!returnValue) { return; } console.log(returnValue); //Add your processing logic here }, function(e) { //put your error handler here });
So far so good. When this code is called following dialog window is shown:
Next part is the extraction of parameters passed to the webresource. Firstly I tried getQueryStringParameters of GlobalContext in two combinations – GetGlobalContext().getQueryStringParameters and Xrm.Utility.GetGlobalContext().getQueryStringParameters and both calls did not bring the expected result. So… the last resort is explicit parsing of the URL. The following code can be used for parsing:
function getUrlParameters() { var queryString = location.search.substring(1); var params = {}; var queryStringParts = queryString.split("&"); for (var i = 0; i < queryStringParts.length; i++) { var pieces = queryStringParts[i].split("="); params[pieces[0].toLowerCase()] = pieces.length === 1 ? null : decodeURIComponent(pieces[1]); } return params; }
Now let’s make this dialog look closer to standard dialogs and add “OK”/”Cancel” buttons to it. There are multiple ways of doing this. I decided to use “Office UI Fabric” for this purpose. Here is the code of the dialog page:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Modal Dialog</title> <link rel="stylesheet" href="https://static2.sharepointonline.com/files/fabric/office-ui-fabric-js/1.4.0/css/fabric.min.css" /> <link rel="stylesheet" href="https://static2.sharepointonline.com/files/fabric/office-ui-fabric-js/1.4.0/css/fabric.components.min.css" /> <script type="text/javascript" src="../ClientGlobalContext.js.aspx"></script> <script src="https://static2.sharepointonline.com/files/fabric/office-ui-fabric-js/1.4.0/js/fabric.min.js"></script> <style type="text/css"> .footer { position: fixed; bottom: 0; right: 0; padding-bottom: 10px; padding-right: 10px; } .footerButton { width: 150px; } </style> <script type="text/javascript"> function onPageLoad() { var urlParams = getUrlParameters(); var cancelButton = document.getElementById("btnCancel"); new fabric['Button'](cancelButton, function() { window.close(); }); var okButton = document.getElementById("btnOK"); new fabric['Button'](okButton, function(){ //Put any logic you want here window.returnValue = "Put the data you want to return here"; window.close(); }); } </script> </head> <body onload="onPageLoad();"> <div class="footer"> <button class="ms-Button ms-Button--primary footerButton" id="btnOK"> <span class="ms-Button-label">OK</span> </button> <button class="ms-Button ms-Button--primary footerButton" id="btnCancel"> <span class="ms-Button-label">Cancel</span> </button> </div> </body> </html>
Here is how it looks like after modifications:
The following code can be used to close it:
window.close();
This is awesome!!
Thank you.
Marcio,
You are welcome!
Andrew
Hi Andrew, note that in chrome the title is in h1#defaultDialogChromeTitle.
They should really provide a way to set the title on load (even the webresource has another name…)
Thanks, Thomas. That’s why code that used to work earlier doesn’t work anymore.
Andrew
Regarding Header of the Dialog window .. i was able to specify the display name in the Solution . (Web resource display name in the solution/customization window).
Hi.. Andrew, Thanks for this post. It was really helpful .. Regarding Header of the Dialog window .. i was able to specify the display name in the Solution . (Web resource display name in the solution/customization window).
Thanks,
Sumit
This doesn’t seem to work on Chrome?
The Display name of the Web Resource is not the title for me.
I can confirm – that’s why I use that DOM manipulation that fixes the header.
Andrew
How can I access Xrm.WebApi within the HTML resource?
So that I can create a Note (annotation Entity) when someone clicks “OK”.
Thanks!
And/Or set a record to another state.
At the moment keeping in mind deprecation of ClientGlobalContext.js.aspx this fall your best chance is either usage of Xrm.WebApi through parent.Xrm.WebApi or usage of pure XrmHttpRequest from the context of the webresource.
Data parameter is no more supported. Can you Please check as it is crashing when we send parameters as shown above
Passing data is not more supported as per document. Can u Please suggest any way to pass as above code is not working for passing data to web resource
Harkamal,
I checked what you said and everything works fine for me. If it works for me and doesn’t work for you I believe there could be an issue in your code.
Andrew
Hi there,
instant of parent … you can us it like this
Xrm.Navigation.navigateTo(dialogParameters, navigationOptions).then(
(result) => this.handleChangeResult(result),
(error) => this.handleError(error));
And on the html dialog you can set:
window.returnValue = { success: true, phonenumber: this.result};
So this should be completely supported.
Andre, thanks for your comment. In my post I use parent for manipulation with Title and for closing of the dialog and not for sending the result back to the calling code. Is there any alternative to that?
Andrew
I use this earlier too, but there is a porblem by validating the code with MS tools, because parent is unsupported.
So I change to set window.returnValue and close the window.
inside the successhandler, the result is this returnValue you set to window.returnValue
Andre, I got that point.
What I meant – are there any supported alternatives on how to close the window from the code or how to change the title in the supported way (i.e. without using parent)?
Andrew
sorry I can’t answer your replay.
Now I got your problem, I don’t think so because what you call ‘title’, should be somthing like ‘address’. I think this is not well thought out by microsoft right now, maybe there will be a update some time.
Andre,
I tries what you suggested and it worked like a charm! Thanks a lot for the tip. I updated my post on development of Dialog Pages here – https://butenko.pro/2020/04/22/development-of-custom-html-js-webresources-with-help-of-modern-frameworks/
Thanks,
Andrew
Andrew, good morning.
Could you help me?
I’m doing with html/js and using your code to open dialog.
Until now, everything is fine, but i found a problem. After I fill data, we have to do some processes.
My old dialog uses ‘Start child workflow, Change record status to, create related entity’.
How do I do this in custom code?
I’m reading about and looks like ‘Workflows will deprecated’.
So, i’d like to ask to you what can I do or what you’d do.
1. Do you think if I use some ‘webapi.update field’ to fires a plugin (to do above operations of old dialog’, will it work in future?
2. Sdk.rest will be deprecated?
3. Do you recommend WebAPI? If yes, do you know if old bugs with external webresources still exists? Below:
Example:
window[“ENTITY_SET_NAMES”] = window[“ENTITY_SET_NAMES”] || JSON.stringify({
“contact”: “contacts”,
“email”: “emails”,
“account”: “accounts”
});
window[“ENTITY_PRIMARY_KEYS”] = window[“ENTITY_PRIMARY_KEYS”] || JSON.stringify({
“contact”: “contactid”,
“email”: “emailid”,
“account”: “accountid”
});
4. I had to use these code above in another situation, because i couldn’t retrieve or create record without to do this. Do you think this will be fix in the future?
Thanks!!
Felipe,
1. I would recommend to put your logic to action and call it using WebApi.
2. I believe Sdk.Rest is a wrapper on top of old odata2 endpoint – and it would be deprecated some time so it’s time to switch to WebApi.
3. I do recommend usage of WebApi. I know what you’re talking about and you have to either use this approach or use parent.Xrm.WebApi… I know that usage of parent is not recommended but at the moment in some situations it’s the most optimal way.
Andrew
Ok. I understand!
So, about action, you mean, create an action on process (like workflows) e call using webapi? Could you give me a example or some information to read? Because i’m new in this part.
Thanks!!
Felipe, sure.
Here is how you can call the plugin when an action is invoked – https://www.magnetismsolutions.com/blog/dominicjarvis/2017/09/18/how-to-trigger-plugins-on-custom-messages-using-actions-in-dynamics-365
What you will have to do is to place your logic inside your plugin (writing the code and no fancy UI…)
To build the code that calls action using WebApi I highly recommend CrmRestBuilder – https://github.com/jlattimer/CRMRESTBuilder/releases/tag/2.6.0.0
Feel free to ask more question if you have issues.
Andrew
Many thanks, Andrew. Helped me a lot!!
I did connection between action interface-plugin code, did logic too, described on link.
But i still don’t get it how to call Xrm.WebApi.online.execute with parameters, sorry :/
Because i’m looking everywhere, but i didn’t find how to pass parameters, any types (lookup, entity, string, int).
And i still one doubt. Do I need to declare input/output parameters in both action interface and plugin code or just in one of it?
Felipe,
Have you tried CrmRestBuilder to prepare initial code for usage of Xrm.WebApi?
In action interface you should define Input/Output parameters and to access values of inputs/set outputs from code of plugin you will have to use context – https://butenko.pro/2013/10/31/actions-usage-of-inputoutput-arguments-in-plugins-that-handle-actions/
Andrew
Many thanks Andrew,
We have this xrm.navigatation.navigateto in a lookup, but the Javascript code that should load within this form is not loading.
Do you know any way to loadto trigger this OnLoad – OnSave Javascript in the dialog form?
Thank you!
Victor,
I’m not sure what you mean. Can you please describe your scenario, what you did and what issue you experience.
Thanks,
Andrew
Hi Andrew,
Yes, sorry!
So we have the entity account, and inside it we have a list of contacts.
If we click in one record of this list, a modal dialog is opened (we are using the navigateTo.)
In our contact form we have some Javascript code inside the onLoad event
function OnLoadContactForm() {
alert(‘I am the contact’)
}
However, when we click in the contact row, the modal dialog is opened, but that alert is not shown.
Does that make sense?
Thank you!
Victor,
Did you attach this OnLoadContactForm handler to OnLoad event of the form load of contact and published changes?
Andrew
Yes, the onLoad is working perfectly on the contact form, but the event is not working with the dialog form way.
Thank you,
Victor
Victor,
I haven’t tried this scenario. I will take a look and be back to you.
Andrew
Andrew,
Many thanks! We think it is a limitation, because it is opened as a section tag inside the HTML, not an iframe, so it seems the Javascript is not loaded, but we didn’t know if you could pass a parameter or something like that.
BTW I really appreciate how quick you replied to my comments.
Thanks!!
Hy Andrew,
i have took your code reference in my example over there actually what i am dong is, i have created that dialog on lead form and on dialog i give drop down to user after the select on option and do click on OK the dialog will be closed and one filed will be update on lead..so for update i have created odata query but i dont know how can i return it
please can you help?
Thanks & regards
Yash,
Not sure what you mean with “return odata query”. Return the value to the calling code and call webapi there.
Andrew
Means on okButton i am updating one of lead form filed using odata query but it’s not working…
Yash, what exactly are you doing? I don’t have superpowers to read your mind or see your code. Andrew
Hy Andrew
Here is code:
var okButton = document.getElementById(“btnOK”);
new fabric[‘Button’](okButton, () =>
{
debugger;
// window.returnValue = “place here data or object you want to return”;if(select.options[select.selectedIndex].text==”–Select–“)
{
alert(“Please select any “);
var select = document.getElementById(“DropDown”);
select.onchange = function(){
selectedOne = select.options[select.selectedIndex].text;
alert(selectedOne);
if(selectedOne==”customer” || selectedOne==”partner”)
{
var clientUrl = window.parent.Xrm.Page.context.getClientUrl();
alert(clientUrl);
var ctyppe ={};
if(selectedOne==”customer”)
{
ctyppe.ewt_custtype=1;//
}
else
{
ctyppe.ewt_custtype=2;//ECC6
}
//Update SAP Source on lead
var req1 = new XMLHttpRequest();
req1.open(“GET”, window.parent.Xrm.Page.context.getClientUrl() +
“/api/data/v9.1/leads?$select=ewt_custtype&$filter=leadid eq ‘”+ leadRef + “‘ “, true);
req1.setRequestHeader(“Accept”, “application/json”);
req1.setRequestHeader(“Content-Type”, “application/json; charset=utf-8”);
req1.setRequestHeader(“OData-MaxVersion”, “4.0”);
req1.setRequestHeader(“OData-Version”, “4.0”);
req1.onreadystatechange = function () {
if (this.readyState == 4 /* complete */) {
req1.onreadystatechange = null;
if (this.status == 200 || this.status == 204) {
//success callback this returns null since no return value available.
var result = JSON.parse(this.response);
// Xrm.Utility.closeProgressIndicator();
} else {
//error callback
var error = JSON.parse(this.response).error;
}
}
};
req1.send(JSON.stringify(ctyppe));
}
}
}
}
Yash,
Just return selected values to the calling code and don’t call the webapi in the dialog – call webapi in the calling part (ribbon).
Andrew
use ‘window.close()’ to close the webresource dialog
before that, set the ‘window.returnValue’ with required data to return .
Thanks a lot! That worked like a charm!
Andrew
The Last sentence: In order to return data from dialog set returnValue of window object in dialog and include parameter to “resolve” handler of promise in calling code. When I try that with a webresource the returnValue in the promise is null.
Am I doing something incorrectly? Thank you!
Larry,
Can you upload the code of your Html/Js webresource somewhere and leave the link here? It’s a bit hard to help you without seeing the code.
Andrew
Xrm.Navigation.navigateTo(pageInput, navigationOptions).then(function (success) {
var selectedDateTime = success.selectedSlot;
//additional code
}
//in the webresource
window.selectedSlot = { date: newDate, start: startTime, end: endTime, isException: isException, };
Try following:
Xrm.Navigation.navigateTo(pageInput, navigationOptions).then(function (success) {
var selectedDateTime = success.returnValue;
//additional code
}
//in the webresource
window.returnValue = { date: newDate, start: startTime, end: endTime, isException: isException, };
Thank you for the quick response! I see the success parameter has a returnvalue in its object, I tried what you suggested but unfortunately returnvalue just stays null
Larry,
Can you please show the code that you use to set the returnValue and close the dialog window?
Andrew
I am so sorry it worked! Thank you so much!! This was a life saver!
In order to get the widow object return value i need to use navigateTo right? Navigation.openwebresource wont return anything?
//example
var winobj = Xrm.Navigation.openWebResource(src, 1000, 800);
winobj.onbeforeunload = function () {
var selectedDateTime = winobj.returnValue;
//additional code
//in webresource
window.returnValue = { date: newDate, start: startTime, end: endTime, isException: isException, };
Larry,
Article is about Xrm.Navigation.navigateTo and it’s the intended usage. You can use Xrm.Navigation.openWebResource but it wouldn’t provide you the modal dialog.
Andrew
Hi Andrew!
Can we do something like that or at least like with the old and unsupported Xrm.Internal.openDialog(…) for the on-premise 9.0 environment?
Thank you.
Hello Danylo,
Absolutely. Check one of my ancient posts on this – https://butenko.pro/2014/06/13/dynamics-crm-2013-step-by-step-creating-dialog-windows/
Also in 9.0 Microsoft introduced new openDialog method under Xrm.Utility namespace that is generally speaking is a wrapper on top of Xrm.Internal.openDialog. You check example by following url – https://butenko.pro/2017/11/21/microsoft-dynamics-365-v9-0-whats-new-in-clientside-for-devs-besides-xrm-webapi/ also I’m not sure that it’ll work in UCI interface but it definitely works in Classic so you can use it.
What you can check is well are following posts from marvelous Bob Guidinger:
https://bguidinger.com/blog/custom-dialog-boxes-part-1
https://bguidinger.com/blog/custom-dialog-boxes-part-2
Thanks,
Andrew
Hi!
Unfortunately, Xrm.DialogOptions() with Xrm.Internal.openDialog() are not working for the 9.0 on-premise, it gives me an error with the reason that DialogOptions is not a constructor. Also, Xrm.Utility.openDialog() is not working, because it is not a function…
I have a custom HTML web-resource that I need to open in a modal dialog, and Bob Guidinger has not described how to do it with his great approach.
It seems like the only way to open custom web-resources in on-premise environments is to use Xrm.Navigation.openWebResource(), but it’s so weak. 🙁
Danylo,
Where do you try to call those methods from? Is it other custom Html/Js webresource? It worked fine when I used it from form or ribbon script.
Andrew
Andrew,
I tried to call them from the Ribbon button script and got errors. 🙁
Could you tell please what exactly worked fine for you from a ribbon script? Is it on-premise 9.0 env.?
Hi Andrew,
This can be used to open a N:N entity relationship view, select records and they appear in a view in the current form?
Ville,
Hypothetically – yes but I haven’t done that yet. Isn’t it would be easier to use Xrm.Utility.lookupObjects to achieve your goal?
Andrew
Hi Andrew, thanks for your response… i thin so, but my problem is that in my old fetchXML view i was having a link entity, but with the lookupObjects im not sure if it supports or maybe i don’t know how to add that link entity filter to the lookupObject filter…
Hi Andrew,
Maybe when you published this article, the way to capture the parameters used in the getUrlParameters () function would return something in var queryString = location.search.substring (1); but currently CRM 365 online V9, it does not return anything.
What way is there at present to recover the parameters?
Hi Andrew, we applied you solution and it worked like a charm until the end of last week.
It appears the returnValue is no longer sent back to the NavigateTo function.
Do you know if something happened or if this implementation was unsupported?
Thanks.
Hello Marc,
I’m not sure if it is supported or not. It’s not documented for sure so it could be considered as unsupported customization so Microsoft could drop it without any notice. I would suggest refactoring your code and use some other technique to pass the data back. For example, using sessions, as I used it before the returnValue was dropped.
Andrew
Hi Andrew,
Yes, using sessions is a workaround (unsupported according to Microsoft’s support engineers but OK to use).
The only problem is that it seems to be slower than passing the returnValue (or maybe the whole callback process is slower now).
Also, official documentation states that we shouldn’t expect any value to be passed to success callback after closing an HTML web resource.
We’re still waiting for Microsoft’s answer for the best way to implement this use case.
Marc,
Feel free to post your results here once you got your question answered by Microsoft.
Thanks,
Andrew
After nearly 3 months, Microsoft announced that the return value is populated again after last weekend’s update (at least in Europe).
I’ve tested that today and it actually works.
I have to admit that I was in contact with one of the best, if not the best, support engineers ever. And he followed the ticket until he got a resolution.
However, it’s not yet clear what was done on the backend side and if this will be supported (if it’s not supported and I have this level of service, it’s fine by me).
Marc
Marc, thanks for the heads up! This is awesome news!
Hi Andrew,
Could you maybe post an example to achieve return parameters using sessions – in case it already exists , could you provide the link please?
Thanks
Ash
Ash,
I updated the code in the post to use sessionStorage to return the result to the calling code.
Andrew
Hello Andrew,
Please can you give me an advice,
I have ribbon button on a entity form. Clicking that button i call Xrm.Navigation.navigateTo and open HTML webresource as a dialog window. How can i get formContext in that webresource script?
Hello Kirill,
Unfortunately, there is no easy/good/supported way to transfer the context at the moment.
What exactly do you want to get from the context?
Andrew
Just field values. As a possible solution, we can use global variable and set it on ribbon button command function:
top.FormContext = formContext;
and use it in webresource script code:
top.FormContext.data.entity.getId()
I just checked it and it works!
But i’m not sure is it a supported way or not…
Maybe better idea is to pass to webresource only ID of currently opened record and then query all required fields with Xrm.WebApi.
What is your opinion, which variant is better?
Thanks!
Kirill,
That’s definitely not a supported way.
If you want to pass the Id – just use the data parameter mentioned in my article and use WebApi to retrieve the values you need.
Andrew
Thank you so much for your help!
My pleasure!
Hi Andrew,
I have ribbon button on a entity form. Clicking that button i call Xrm.Navigation.navigateTo and open HTML webresource as a dialog window. I have 3 buttons on the HTML webresource and out of 3 buttons I need to call an action to send notification but I am unable to achieve it as I am unable to get the formcontext by any means in the webresource or in the function that is called on button click. Please help
Shrikant,
You can’t access formContext from the webresource code, unfortunately. You will have to return the result to the calling code – ribbon button handler and access formContext from that part.
Andrew
I would like to hide navbar and cmdbar and open the mainform in readonly mode in side Popup
Hello,
You can use navigateTo to show the record form in the popup but to show the form in read-only mode and hide cmdbar you will have to add some scripts to the form itself. Check this post from Diana – https://dianabirkelbach.wordpress.com/2020/10/14/transform-forms-into-dialogs-and-more/
Thanks,
Andrew
When I use the data parameters copied just as you have it, I get my data like this: {data: “[object Object]”} any ideas?
Hello,
Can you please provide the code that you use to open the dialog, i.e. where you compose the URL of the dialog.
Thanks,
Andrew
Hi Andrew,
I had the same issue.
I believe the data object we pass to the pageInput cannot be an object like this – but we can only send a string.
JSON.stringify() should work – but haven’t tested.
Cheers
Hello,
I believe that’s the reason. I just wanted to be sure seeing the code.
Thanks,
Andrew
Hi!
There now seems to be a “title” parameter in the reference under navigationOptions – “title: (Optional) String. The dialog title on top of the center or side dialog.”
https://docs.microsoft.com/en-us/powerapps/developer/model-driven-apps/clientapi/reference/xrm-navigation/navigateto
But it did nothing when I tried it out. 🙂
Hello,
It worked for me and I have already used it in my ABDialogs project. It will be included in the next release of the project.
Thanks,
Andrew
Hi Andrew,
I’ve seen that PowerApps now has “Custom Pages”.
The navigateTo function also supports custom pages to navigate to.
https://docs.microsoft.com/en-us/powerapps/developer/model-driven-apps/clientapi/reference/xrm-navigation/navigateto
Can they be used instead of HTML Webresource Popups?
Do you think they will replace those Custom HTML popups eventually?
Hello Serfi,
I believe that approach could be used as an alternative to Html webresources.
Diana wrote a wonderful post that describes how Custom Pages could be used in Dialog scenarios – https://dianabirkelbach.wordpress.com/2021/07/29/how-to-make-dialogs-for-model-driven-apps-using-custom-pages/
Andrew
Is it possible with the Quick Create form?
Hello Mark,
Not sure what you mean by that. Like opening additional html/js webresource from Quick Create Form?
Andrew