Overloading Methods With JavaScript Control Add-ins

Switching from C# to JavaScript to develop your control add-ins might get you scratching your head more often that your scalp, or nails for that matter, might be happy with.

One of those is overloading. In C#, this is a no-brainer:

However, when you want to do that in JavaScript, if you are not a JavaScript developer, making this work is not as straightforward as a regular JavaScript Joe would find it.

So, consider this C# interface for the JavaScript control add-in for a second:

This, for sure, won’t get the job done:

The reason why not is that all the calls below:

image

… will result in exactly the same result:

image

The problem (well, not really a problem) with JavaScript is you can’t overload a function. Just like C/AL, you can only have one function with any given name – it’s the name that defines the function, not its signature. However, JavaScript runtime won’t complain if you declare three functions with the same name – it will simply overwrite the existing function of the same name. It’s not a bug, or a misbehavior, or something of the sort. On the contrary, it’s how it’s designed.

In JavaScript functions are really variables. So, as much as you are allowed to do this:

… you are also allowed to do the same with the functions. My code above with three functions is in not really way too different than this:

And this explains just why no matter with which parameters I call the HelloWorld function, it is always calling the last function assigned to that name. The others are gone, out of scope.

Being a C/AL guy/gal and all, consider it for a moment – you’ll find this feature beautiful – I promise.

There is an even nicer feature of JavaScript that you’ll quickly start to love – in JavaScript you don’t have to really match the types and numbers of arguments when calling functions. No matter how many parameters the function declares, you can call it with an arbitrary number of arguments.

And here lies the key to overloading. C# needs to overload the function in the interface definition, because this allows C/AL to call the (same) function with different arguments, but JavaScript only ever needs one definition alone. Something like this (not to say that this is necessarily the only, or even the best way):

image

Then, your C/AL code can call any of the three interface overloads, and it will always be the same JavaScript function responding. Then, from inside the function we check the number (and/or types) of arguments to decide what exactly to do or return.

And that’s it. Simple, and elegant.

Vjeko

Vjeko has been writing code for living since 1995, and he has shared his knowledge and experience in presentations, articles, blogs, and elsewhere since 2002. Hopelessly curious, passionate about technology, avid language learner no matter human or computer.

This Post Has 9 Comments

  1. strangenikolai

    Hi Vjeko

    I’m having an issue with this (getting the return value back from a Javascript).

    This is in the C# bit:
    [ApplicationVisible]
    string HelloWorld();

    This is in the Javascript:
    function HelloWorld() {
    debugger;
    return “Hello World”;
    }

    And this is what I’m calling from C/AL:
    MESSAGE(CurrPage.Ctrl.HelloWorld());

    The javascript debugger is firing up and I can see the return value, but I just get a blank message showing in NAV.

    Events and pass into Javascript functions are all working so I suppose I could turn this into an asynchronous pattern – but it seems odd.

    Any clues?

    Thanks

    Nikolai L’Estrange

    1. strangenikolai

      I may have answered my own question – when I follow the Javascript debugger a bit deeper I see how the Javascript functions get called from the NAV application – through some functions that are hardcoded into the auto-generated html files that get copied to your machine.

      Specifically in this case it goes into the InvokeScriptMethod function – which doesn’t have a “return” anywhere. Annoying.

      function InvokeScriptMethod(methodName, args) {
      var frame = document.getElementById(“controlAddInFrame”);
      frame.contentWindow.Microsoft.Dynamics.NAV.InvokeScriptMethod(methodName, args);
      }
      function InitEnvironment(deviceCategory, companyName, userName) {
      var frame = document.getElementById(“controlAddInFrame”);
      frame.contentWindow.environment = new Object();
      frame.contentWindow.environment.DeviceCategory = deviceCategory;
      frame.contentWindow.environment.CompanyName = companyName;
      frame.contentWindow.environment.UserName = userName;
      frame.contentWindow.environment.Busy = false;
      frame.contentWindow.environment.OnBusyChanged = null;
      }

  2. strangenikolai

    And to continue answering myself – it doesn’t help if you change* the InvokeScriptMethod functions (there are two) to return a value, whatever is calling these (windows.external) is not returning anything to NAV.

    I’m guessing this is on purpose because the web client will fire these methods as something like async AJAX calls so a return value won’t work… this is complete conjecture though…

    So it looks like I will need to fire events instead (i.e. windows.external.Notify)… that’s what all the standard examples do isn’t it? OnControlReady etc…

    *don’t ask how I’m changing the hardcoded html, I’m doing sneaky things…

    1. Vjeko

      Nikolai, this is not possible and it’s even documented to not be possible. You can only pass parameters to JavaScript, but you cannot receive return values from JavaScript. All JS is executing asynchronously. When JavaScript needs to “return” a value to C/AL it must call an event.

      1. strangenikolai

        Thanks Vjeko, I thought that might be the case but couldn’t find the documentation. Just found it – i was looking at the MSDN pages for “Windows Client Add-Ins” and you have to go back up a few levels to find “Any Client Add-Ins”.

        I also thought it was worth a try as I found examples of other apps that use a Webbrowser control and get return values from Javascript…

        1. Vjeko

          Yes, it is documented. You must have missed all my presentations about JavaScript control add-ins, I’ve always mentioned this limitation 😉 You are right – this is not a limitation of JavaScript, it’s simply how NAV interacts with JavaScript. I have complained to MS about this, and I still hope a future release may bring a solution to this limitation.

  3. Peter

    Hi Vjeko,

    I’m having trouble even getting NAV 2016 to recognize my javascript functions.

    I have no problem with adding some kode into $(document).ready(function() { … });
    but if I have a function defined (doesn’t matter if it is before or after document.ready in the CDATA field of the manifest) and try to call it from say a button, then the browser shows that the method is undefined.

    e.g.

    // this is inside document.ready event handler

    $(‘#controlAddIn’).append(”);

    // and this is outside document.ready event handler, but still within the CADTA field in the manifest.

    function clearText()
    {
    $(‘#taEditor’).val(”);
    }

    Clicking the button will get me a message saying that clearEditor is undefined.

    Had I instead (inside document.ready event handler) done this:

    $(‘#btnClear’).click(function()
    {
    $(‘#taEditor’).val(”);
    });

    Then it works just fine.

    Do you have any idea as to what is causing this?

    Also, is there a way to get around NAV changing backslashes into forward slashes or is the a security thing?

    1. Vjeko

      You have a scoping issue here. Your function clearText is defined outside within an inner scope and is visible only from that scope. The whole CDATA section executes from within a closure of its own, and to make this function visible outside its scope you must assign it directly to the window object, or you must put it outside of CDATA section. My suggestion is: move all CDATA code into a js file that you include through the section of the manifest – then you’d not have this issue.

Leave a Reply