I recently ran into an annoying issue with JavaScript web resources not loading correctly with one of my customer’s entity forms. Basically, a utility function was working correctly with the form OnLoad event but when the OnChange event fired for an attribute on the form fired, the very same utility function was not available.
The solution includes form scripts and several utility libraries, where form scripts have dependencies on the utility script libraries. Standard stuff for CRM solutions. My customer also uses the recommended method of namespaces to avoid clashes with multiple scripts on a form. Check out the section titled Namespaced library names in this MSDN article, JavaScript libraries for Microsoft Dynamics CRM. Here are two additional helpful posts on namespaces, Namespaces in CRM JavaScript by Jason Lattimer (Holy cow, his CRM Rest Builder is a massive time saver!) and {Best Practices} Naming convention for you javascript webresources in Dynamics CRM by Debajit Dutta. Both offer a quick overview of namespaces if you haven’t used them before. Since my customer uses them throughout their system consistently, I was surprised to see this issue pop up.
The problem
The error occurred with a simple string helper method that formats a GUID for a Web API call. This method is part of a strings utility library where we use the company abbreviation as the root namespace, then strings as a child namespace. So calling the method looks something like: corp.strings.cleanGuid(guidVal)
. As mentioned, the form scripts have a dependency on the utility libraries and our function call worked with form OnLoad. When the same method was again called during an attribute OnChange event, we received an error that the strings namespace did not contain our method cleanGuid()
. The dreaded null or undefined script error! So, what was going on?
Some debugging
Kicking open the debugger in Internet Explorer, I checked the loaded script libraries thinking I did not deploy something correctly, but everything looked fine with both the form and utility libraries loading correctly. I was able to search using the debugger tools for our corp.strings.cleanGuid()
method and found it right way. I added some break points to see if maybe we had some kind of odd timing issues, but that turned up nothing. I published again, deleted my client side cache, and reloaded everything, thinking maybe by some slim chance, it was a client issue… but that didn’t fix things either.
Then things really started looking odd. It looked as if an old version of the utility script was being loaded! What made me say this was that when looking at the debugger object tree during the attribute OnChange event, lots of updated methods were missing from the corp.strings
namespace. I had no typo parsing errors or simple loading errors from the web resources. So needless to say, this was a bit confusing. Searching a bit further in the loaded resources, I found another instance of the corp.strings.cleanGuid()
method loaded by the browser. It was not part of a web resource or other CRM static script, but it was in the debugger document tree under Dynamic scripts and labeled “script block”. In the debugger, it looks something similar to this:
This meant that CRM was dynamically injecting the script block into the page, just like it does with scripts that are related to custom ribbon buttons.
And there it was: We had a copy of the entire strings namespace in the form’s ribbon script web resource! This would normally not cause issues, but it was out of date and did not include our new corp.strings.cleanGuid()
method. So when the browser parsed the older copy of the strings namespace, it overwrote the newer object and wiped out our method!
The Tips and Tricks!
So why did we have a copy of the entire strings namespace included as part of the form ribbons library in the first place? If we had the shared library, why not reference it since copying and pasting scripts can lead to issues? My customer’s current solution is being upgraded from CRM 2011 (see my CRM 2011 to CRM 2016 blog post series!) and when making the upgrade from CRM 4 to 2016, the new ribbon bar was used frequently. When defining ribbon commands, we saw that methods contained in script libraries loaded with the form were not readily available to scripts loaded with the ribbon. For example, if I were to load a utilities.js script resource as part of my form definition, all functions such as corp.strings.cleanGuid()
would be available is accessible form events such as OnLoad, OnSave, attribute OnChange, etc. But if I tried to access utilities.js functions from a ribbon button action, I received an error.
Unfortunately, there was no easy way at the time to simply add more than one library reference when creating the ribbon command action as you can do in the CRM form designer. In order to get the ribbon actions working, my customer had to merge their utility scripts with their ribbon scripts, and they eventually moved out of sync. It turns out, there IS a relatively easy method for referencing an additional library web resources from a ribbon commands. Thanks to Sean McNellis for this tip: you just add more than one command action! But… that seems too simple. And if we add more than one action to the command, what will they do? We usually only want to call one function when a button is clicked, so how would adding multiple actions work?
I will assume you are familiar with some of the ribbon editing tools available for Dynamics CRM, and for this discussion, I’m going to use the Ribbon Workbench developed by Develop 1 Limited and Scott Durow (check out both sites for some great CRM information!). I’ll add a simple button to the Account so that we can take a look at the button command definition and its related actions.
Here you can see that I’ve just added one simple button and a new button command. With each command in the ribbon, you can add one or more actions, either calling a JavaScript function or opening a URL. When adding a JavaScript action, we specify a function to be invoked, the web resource in which the function is located, and any parameters that need to be passed. So in this simple example, I want to call a function in our accounts main form script.
This is the standard stuff when defining a ribbon button: add your button, call the action by defining your command. Now in my example, lets assume that my new corp.account.buttonOneClick
method depends on a helper function contained in a different web resource. I don’t want to actually invoke any of those methods in my command action, I just want to use them in corp.account.buttonOneClick
. To accomplish this, we can just call a generic function that will not do anything nor cause any errors. For example, isNaN
is a core JavaScript method, and we can call it while reference our utility web resource:
Here I’ve added the new function ahead of the actual ribbon command that will do the work so that it’s loaded first, assuming that it will be injected into the script block in order (although, given some of the recent form rendering engine changes, you never know). We are now assured that this script library will be loaded and available to our ribbon command actions! This also keeps our web resources separated and much easier to maintain and reuse. I like this solution even if it adds a few steps to working in the ribbon editor.
But I am still a bit confused as to why the issue popped up in the first place? As I mentioned, we had issues with ribbon scripts accessing methods available in web resources loaded by the form, hence the need for a solution like this one. Yet it this error popped up here because ribbon scripts overwrote objects in scripts loaded by the form! Anyone have suggestions on why this would be the case? I have a bit more digging to do and if I come up with an explanation, I’ll update the post.
As always, comments, questions, and corrections are always welcome!