Blog, Development

Xrm.Navigation.navigateTo – gotchas, tricks and limitations

Here are ways that were used by developers to show Modal Dialogs:

  1. Xrm.Internal.openDialog
  2. Alert.js
  3. 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();

87 Comments

  1. 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…)

    1. Thanks, Thomas. That’s why code that used to work earlier doesn’t work anymore.
      Andrew

    2. 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).

  2. 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

    1. This doesn’t seem to work on Chrome?
      The Display name of the Web Resource is not the title for me.

      1. I can confirm – that’s why I use that DOM manipulation that fixes the header.
        Andrew

  3. How can I access Xrm.WebApi within the HTML resource?
    So that I can create a Note (annotation Entity) when someone clicks “OK”.

    Thanks!

    1. 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.

  4. Data parameter is no more supported. Can you Please check as it is crashing when we send parameters as shown above

  5. 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

    1. 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

  6. 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.

    1. 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

      1. 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

        1. 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

        2. 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.

  7. 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!!

    1. 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

      1. 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!!

        1. 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

  8. 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?

  9. 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!

    1. Victor,
      I’m not sure what you mean. Can you please describe your scenario, what you did and what issue you experience.
      Thanks,
      Andrew

      1. 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!

        1. Victor,
          Did you attach this OnLoadContactForm handler to OnLoad event of the form load of contact and published changes?
          Andrew

        2. Yes, the onLoad is working perfectly on the contact form, but the event is not working with the dialog form way.

          Thank you,

          Victor

        3. Victor,
          I haven’t tried this scenario. I will take a look and be back to you.
          Andrew

        4. 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!!

  10. 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

    1. Yash,
      Not sure what you mean with “return odata query”. Return the value to the calling code and call webapi there.
      Andrew

      1. Means on okButton i am updating one of lead form filed using odata query but it’s not working…

        1. Yash, what exactly are you doing? I don’t have superpowers to read your mind or see your code. Andrew

  11. 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));
    }
    }
    }

    }

    1. 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

  12. use ‘window.close()’ to close the webresource dialog
    before that, set the ‘window.returnValue’ with required data to return .

  13. 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!

    1. 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

      1. 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, };

        1. 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, };

        2. 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

        3. Larry,
          Can you please show the code that you use to set the returnValue and close the dialog window?
          Andrew

        4. 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, };

        5. 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

  14. 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.

    1. 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

      1. 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. 🙁

        1. 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

      2. 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.?

    1. 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

      1. 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…

  15. 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?

  16. 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.

    1. 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

      1. 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.

        1. Marc,
          Feel free to post your results here once you got your question answered by Microsoft.
          Thanks,
          Andrew

        2. 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

      2. 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

        1. Ash,
          I updated the code in the post to use sessionStorage to return the result to the calling code.
          Andrew

  17. 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?

    1. 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

      1. 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!

        1. 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

  18. 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

    1. 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

  19. I would like to hide navbar and cmdbar and open the mainform in readonly mode in side Popup

  20. When I use the data parameters copied just as you have it, I get my data like this: {data: “[object Object]”} any ideas?

    1. 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

      1. 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

        1. Hello,
          I believe that’s the reason. I just wanted to be sure seeing the code.
          Thanks,
          Andrew

    1. 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

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.