Solving the Client Script ‘Before and After’ Problem

/, System UI, UI macros/Solving the Client Script ‘Before and After’ Problem

Solving the Client Script ‘Before and After’ Problem

E

very now and then I come across a question dealing with the order of execution for client-side code (Client Scripts and UI Policies). In my experience, the need to order client-side code is pretty rare and really only applies in a few ‘onLoad’ scenarios and even fewer ‘onChange’ scenarios. Usually, the way that a browser parses and executes client-side code means that the ordering is pretty unpredictable. It’s worth mentioning that this issue doesn’t exist with server-side code precisely because the browser isn’t involved (which is why you can neatly order all of your business rules without issue).

In this post I’ll show you some of the techniques I’ve used in the past to provide some control over the order of execution of client-side code. I’ll also show you a pretty cool trick that you can use to ensure that a piece of client-side code runs before any other client script or UI policy…or after ALL of those scripts finish running.

Solving the ‘before…and after’ problem

The first thing you should be aware of is that you do have some limited ability built-in to control the order of execution for client-side code with the ‘Order’ field on UI policies.

UI Policy Order

There are a few caveats with the ordering of client-side code though.

  1. Ordering IS NOT available to client scripts, only UI policies
  2. Client scripts will start their execution before UI policies
  3. Even with the order field on the UI policies, it only controls the start order of execution for the scripts. The actual order that various portions of those scripts run depends on the complexity and variety of the scripts so you can’t really guarantee that a particular piece of client-side code will run before or after another.

You can also set up a wait or delay in your script by using the ‘setTimeout’ method (or something similar).

Guaranteeing absolute before…and after…

I was recently asked to help solve a problem with a long-loading form. The form took several seconds to render and hide various form sections, fields, and related lists. The problem was that during this time, users could interact with the form before it was ready to use. This can cause issues with incorrect submissions, mis-routed calls, and the general undoing of all of the great things you designed the form to do in the first place. Before I explain this solution you should know that the overwhelming majority of issues in this area can be prevented by following a few simple rules…

  • Limit the amount of information on your form to what is absolutely necessary. Dozens of form sections and related lists and hundreds of fields on a form just aren’t going to load quickly no matter what you do…even if they’re hidden!
  • Stay away from ‘GlideRecord’, ‘getReference’, and ‘GlideAJAX’ in your client scripts and UI policies! If you do need to use any of these then use asynchronous processing if at all possible!
  • Follow these best practice guidelines in your client scripts.

Sometimes process and bureaucracy win out over performance and sensibility however, and you have to deal with a problem that you would rather avoid in the first place. One idea would be to use the loading dialog that I described in a previous post on ServiceNowGuru. This would prevent user interaction with the form until the entire thing finished loading and all of the scripts had finished their execution.

Long Loading Form Dialog

The dialog is easy enough to execute, but the real problem is guaranteeing that the dialog displays before anything else on the form (which you can’t do with client scripts or UI policies) and knowing definitively when the form finishes loading so that you can close the dialog.

There’s simply no way to guarantee that a client script or UI policy will run before the form loads. The actual fields (and formatters) will be rendered before the scripts run though. The solution I came up with was to create a custom hidden field (by utilizing a UI macro and UI formatter) to execute some code before any other client scripts run. The script is actually very simple. It just shows the loading dialog, uses Prototype’s ‘Event.observe’ function to identify when the form has finished loading, and hides the dialog.

//Show the loading dialog immediately as the form loads
var loadingDialog = new GlideDialogWindow("dialog_loading", true);
loadingDialog.setPreference('table', 'loading');
loadingDialog.setTitle('Loading...'); //Set the loading dialog title here...
loadingDialog.render();
                 
//Wait until the form has finished loading
addLateLoadEvent(function(){
    loadingDialog.destroy();
});

You can set up the UI macro and UI formatter as follows…

‘form_loading_dialog’ UI macro
Name: form_loading_dialog
XML:

<?xml version="1.0" encoding="utf-8" ?>
<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">
   <g:evaluate var="jvar_show_dialog" expression="!RP.isPopup()" />
   <j:if test="${jvar_show_dialog}" >
      <script>
         //Show the loading dialog immediately as the form loads
         var loadingDialog = new GlideDialogWindow("dialog_loading", true);
         loadingDialog.setPreference('table', 'loading');
         loadingDialog.setTitle('Loading...'); //Set the loading dialog title here...
         loadingDialog.render();
                 
         //Wait until the form has finished loading
         addLateLoadEvent(function(){
             loadingDialog.destroy();
         });
      </script>
   </j:if>
</j:jelly>
‘Form Loading Dialog’ Formatter
Name: Form Loading Dialog
Formatter: form_loading_dialog (or the name of your UI macro)
Table: Task
Type: Formatter

Once you’ve set up the UI macro and UI formatter for the table(s) of your choice, all you have to do is personalize the form for any long-loading form and add the ‘Form Loading Dialog’ formatter to the first form section on the form. Enjoy!

Personalize Form Loading Dialog

By | 2018-07-09T14:59:59-06:00 June 13th, 2011|Categories: Client scripts, System UI, UI macros|Tags: , , , |46 Comments

About the Author:

46 Comments

  1. Steve Darity June 13, 2011 at 4:07 am - Reply

    Great Article. Easy to implement and very effective. I like the comment about keeping the form simple as an alternative to using this solution. Limitied use of complex client side scripts and synchronous Ajax calls to the Server.

    Thank You.

    • Mark Stanger June 13, 2011 at 8:47 am - Reply

      Thanks Steve! It’s definitely better to avoid a long-loading form in the first place.

  2. Matt Benson June 22, 2011 at 1:41 am - Reply

    Awesome solution! I love it and it works great.

    The only issue I ran into with is that it didn’t seem to play nicely with reference icons. When hovering over a reference icon the form would begin to load, the dialog would pop-up and then the form would disappear. At this point the loading dialog would be on screen and just sit there, never to stop loading. 🙂

    By removing the form loading dialog from the sys_popup view I was able to solve the issue.

    Thanks again Mark!

    • Mark Stanger June 23, 2011 at 4:24 pm - Reply

      Yep, I noticed that too a little bit after I posted the solution. I updated the script a couple of days ago with a fix. Thanks for the feedback!

  3. Sandy Seegers June 23, 2011 at 5:46 am - Reply

    Mark!

    This seems to solve the number one annoyance I have with ServiceNow!!!

    Thank you so much.

    Now on to number two . . . I’ll let you know when I figure out what it is.

  4. Xavier July 7, 2011 at 7:20 am - Reply

    Hi,

    thanks for this wonderful solution.
    I just want to warn you that with some user and with internet explorer we have an issue, the form is stuck loading…
    There is an error message in ie :

    Message: HTML Parsing Error: Unable to modify the parent container element before the child element is closed (KB927917)

    I will have to remove this loading form from everywhere until I found where it comes from 🙁

    • Mark Stanger July 7, 2011 at 7:27 am - Reply

      Thanks for the feedback. If you can give me more details on where you’re using the script, seeing the problem, and how to reproduce the problem I can try to investigate. My guess is that there’s some conflict with another client script on that particular form but I’ll need more details to even begin to give a good guess.

      • Xavier July 7, 2011 at 7:35 am - Reply

        in fact I cannot give a lot more details as I cannot reproduce it myself :s.

        It only happens with some users, not every users.
        It’s only with internet explorer.

        I use your loading solution on the change_request, change_task, incident and problem.
        For the users impacted, the issue is on every form with the loading form

        • Mark Stanger July 7, 2011 at 7:37 am - Reply

          Let me know if you find anything. The only issue I know of was the issue with hover icon popups (see the comment from Matt Benson). That issue has been resolved however if you use the latest version of the script found in the article above.

          • Xavier July 7, 2011 at 7:41 am - Reply

            Yes, I use the last version of your script…

            thanks anyway

  5. Jim Uomini July 21, 2011 at 4:49 pm - Reply

    The loading dialog is the perfect solution to our overly complex Incident form. Thanks Mark.

  6. Nishant November 17, 2011 at 8:22 am - Reply

    It appears that some users have issues when the form loading box does not disappear. Following error message is displayed:
    “Message: HTML Parsing Error: Unable to modify the parent container element before the child element is closed (KB927917)”
    It appears that the issue is prominent in IE6 and some installs of IE7 & IE8. (IE specific issue).
    Is there any solution for this issue that you could suggest?

    • Mark Stanger November 17, 2011 at 9:56 am - Reply

      I think I’ve seen this issue reported before and it was caused by more than one dialog being displayed at the same time. Check to make sure you’re not calling or displaying multiple dialogs. Other than that, I’ve never seen a problem with the loading dialog unless you’ve got some custom script running that breaks and causes the code to exit before it can close the dialog.

  7. Nishant November 17, 2011 at 3:05 pm - Reply

    I have checked all the scripts on the forms. There are no conflicting dialog boxes. This error shows up only for some users on specific computers. Other users are able to access the form normally.

  8. Brian Broadhurst November 22, 2011 at 8:31 am - Reply

    Hi Mark,

    this is proving really useful on my current implementation assignment. Do you know if it is possible to amend the content of the pop-up? I found an entry in the sys_ui_message table with the Key “Loading…” and changed this – but it doesn’t have any effect. Also, I would like to change the dialogue header (currently “Dialog”).

    Brian

    • Mark Stanger November 22, 2011 at 9:27 am - Reply

      Hey Brian,

      Thanks for the feedback. I’m not aware of any way to easily customize the contents of the generic loading dialog. You can replicate the same functionality with a completely custom dialog though. Your dialog could use whatever UI page you wanted and set the title however you wanted as well.

      Mark

  9. Abhiram Diddigi June 28, 2012 at 4:23 am - Reply

    Hello Mark,

    Great post.

    I was trying to run a script which runs after all the options of Slush Bucket gets filled(This is the ajax call populateSelect()), but wasn’t able to succeed, Can we extend this post to handle my scenario? Thanks.

    • Mark Stanger June 28, 2012 at 5:22 am - Reply

      It’s going to be more challenging with a slushbucket, especially if the slushbucket isn’t a catalog list collector. The only way to get client scripts to run in the regular slushbucket interface is with a UI script. Even then it’s a pretty big hack so I wouldn’t attempt it. If you’re working with a list collector variable then it would be easier, but you still have to attach to several different elements in that interface. I don’t have any solution to do what you’re asking and I would advise against this modification unless it’s something that you absolutely can’t do without.

  10. Order of onLoad vs onChange June 29, 2012 at 4:23 am - Reply

    Hello Mark,

    Thanks for your post, great and useful as always.

    I have a question: do you know if all the ‘onLoad’ scripts are guaranteed to be executed before the ‘onChange’ scripts on page load?

    My use case is the following: I have 2 onchange scripts (on different fields) that have very similar code. I would like to put that common code in a single place, in a function somewhere where it can be called by the ‘onChange’ scripts.
    Normally a UI Script is good for this but it creates another .js file that has to be downloaded by the browser, and is present in every page, not just on the required table.
    So I thought of making an ‘onLoad’ script which stores the function in the “window” object:
    window.myfunction = function( ) { … }
    And then just call it from the onChange scripts:
    … myfunction() …

    And it works well, but I am wondering and I was just lucky that the ‘onLoad’ script is executed before the ‘onChange’ scripts by luck or if it is always like that. If the ‘onChange’ scripts got executed before the ‘onLoad’ scripts, they would use a function that is not yet defined.

    Cheers,
    David

    • Mark Stanger June 29, 2012 at 9:55 am - Reply

      This is a good question, but one that I haven’t proven out an answer for yet so I’ll give you my best guess and a couple of suggestions. 🙂 The solution you’re proposing was recently put into the product to facilitate the incident priority calculation via the impact and urgency fields. However, the onChange scripts in this case are specifically set to not run onLoad so they bypass the potential problem. Another option to consider would be to avoid onChange scripts completely and set up everything (including field-specific onChange handlers) in an onLoad client script. This approach should work because the fields and values should always be in place for the onLoad script. I’ve actually considered changing my assignment and priority lookup solutions to use this type of technique.

      • David June 29, 2012 at 10:18 am - Reply

        Hi,

        With Firebug, I have put breakpoints in a lot of onload scripts and a lot of onchange scripts. All the onload ones got executed before the onchange ones. I also noticed that they are defined differently. The last step would be to go up the function call stack and discover how the Service-Now client-side “core” is calling all the stuff.

        In any case I think it’s pretty safe to assume that the onload scripts get executed before the onchange scripts – and I would doubt that this changes in the future.

        Cheers,
        David

  11. Sandy November 29, 2012 at 2:17 pm - Reply

    Hi Mark,
    I have a business rule on table A. This is an after update business rule.

    One of the columns in table A is a reference to hardware table.

    Action which causes update on Table A, SOMETIMES CAN change hardware status on the CI and when this happens I want business rule to fire after the change to hardware status.

    There is a delay of around 3 seconds between update to Table A and change of hardware status to CI, so I am not able to use new hardware status in my business rule.

    Is there a way I can create some delay inside my business rule or script include so that it can fire 3 seconds after the update – something like setTimeout?

    • Mark Stanger November 29, 2012 at 2:59 pm - Reply

      Hi Sandy,
      This article is all about client-side transactions. If you want a delay in a server-side script, you could try a ‘gs.sleep(3000)’ command or an asynchronous business rule.

  12. Sandy November 29, 2012 at 5:59 pm - Reply

    Thanks so much Mark!
    Can you tell me how I can control async business rule – will it not fire at random time!

  13. Michele King March 6, 2013 at 3:47 pm - Reply

    In Berlin, the Loading dialog disappears before the form finishes rendering/processing the Client Scripts and UI Policies and on Before Business Rules and Display Business rules. Client Transaction shows browser (client scripts) are taking the longest to load but when I disable the two client scripts it does not resolve the issue. Looks like Event.observe(window, ‘load’, function() { doesn’t wait for all the processing to finish anymore. Does that sound right? Is there a new event that would identify when the form is done loading? I have switched the UI Macro to use window.setTimeout at 4 seconds but that won’t help if the form takes longer than that to finish rendering/loading. Any suggestions?

    Thanks,
    Michele

    • Mark Stanger March 6, 2013 at 7:15 pm - Reply

      I haven’t seen this before, but I am able to reproduce it. I think the issue is that ServiceNow isn’t returning the window load event at the correct time anymore. Unfortunately, I don’t know what else you could trigger on. If you figure anything out, please let me know.

      • Michele King March 7, 2013 at 7:18 am - Reply

        This seems to work better…

        [code]
        showLoadingDialog();
        addLateLoadEvent(function(){

        hideLoadingDialog();
        });
        [/code]

        • Mark Stanger March 7, 2013 at 7:25 am - Reply

          Same problem in my testing with this code. I’ve got it set up on the incident form on demo010 right now if you want to take a look.

          • Jani Luostarinen April 23, 2014 at 7:20 am - Reply

            I know this is an old topic and the reply might be a tad late, but anyway here it goes 🙂

            I just had the same issue with the Loading dialog disappearing before the form was loaded. My Loading dialog is called from an onSubmit Catalog Client Script and the script is attached to an Order Guide (actually also to all Catalog items in the Order Guide via Variable set).

            There is two possible solutions for waiting the form to be loaded:

               showLoadingDialog();
               document.observe("dom:loaded", function() {
                  hideLoadingDialog();
               });

            or, if you’re using jQuery:

               showLoadingDialog();
               $( document ).ready(function() {
                  hideLoadingDialog();
               });
  14. Michele King March 7, 2013 at 7:56 am - Reply

    I’m looking at it in demo010 and it seems to be dropping the “Loading” dialog box when the form finishes loading for me. I notice demo010 has a several errors on the pages so maybe that is part of the issue?
    I’m also testing it on a client instance where they have a bunch of client scripts and UI Policies and the fields all “resolve” then the loading dialog goes away.
    And I’m testing this in Internet Explorer.
    Anyway, if that code isn’t going to be reliable then I’ll keep looking.
    Thanks-Michele

    • Mark Stanger March 7, 2013 at 11:35 am - Reply

      Looks like your solution handles most situations, but lots of embedded related lists seem to throw it off for some reason. I’m going to adjust the code above with your fix since that does seem to work better. Thanks!

  15. Abdul Fathah April 5, 2013 at 6:20 am - Reply

    is it possible to visible the Loading Dialog Box, when we change some values in the field?

  16. Felipe Barbosa February 19, 2014 at 8:34 am - Reply

    Mark, once again, excellent solution, kudos for you man.

    It worked like a charm, thanks so much!!

  17. Christian March 14, 2015 at 5:02 am - Reply

    Hi Mark, have not been in touch for ages…. sorry about that, I guess, well meet at K15? I just had a re-read of your post which I have used in the past. Howeer today I am looking to use this for a very complex catalog item… The item (don’t ask) has >200 Fields and Policies an client scripts (and load time) to match. I can’t get this to execute for a catalog item…

    • Mark Stanger March 16, 2015 at 6:51 am - Reply

      It may be a timing issue. You can try using a ‘setTimeout’ call to make sure ‘showLoadingDialog’ is available to call. Something like this in your UI macro maybe.

      <?xml version="1.0" encoding="utf-8" ?>
      <j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">
         <g:evaluate var="jvar_show_dialog" expression="!RP.isPopup()" />
         <j:if test="${jvar_show_dialog}" >
            <script>
               //Show the loading dialog immediately as the form loads
               setTimeout(function(){ showLoadingDialog(); }, 500);
               //Wait until the form has finished loading
               addLateLoadEvent(function(){
                  hideLoadingDialog();
               });
            </script>
         </j:if>
      </j:jelly>
      • David July 14, 2015 at 10:09 am - Reply

        Hello,

        This does not seem to work in FUJI. Is there any other way to make it work? Thanks!

        • Mark Stanger July 14, 2015 at 12:00 pm - Reply

          This issue has to do with the fact that ‘showLoadingDialog’ isn’t available to call right when the form loads due to some changes ServiceNow has made in Fuji. The only way I know of to attempt to work around the issue is to delay the display of the loading dialog. I don’t know if it works 100% of the time, but you can see an example of that type of script at the bottom of the comments section here.

        • HD October 27, 2016 at 6:51 am - Reply

          This works on Helsinki:

          //Show the loading dialog immediately as the form loads
          var loadingDialog = new GlideDialogWindow("dialog_loading", true);
          loadingDialog.setPreference('table', 'loading');
          loadingDialog.setTitle('Loading...'); //Set the loading dialog title here...
          loadingDialog.render();
                           
          //Wait until the form has finished loading
          addLateLoadEvent(function(){
              loadingDialog.destroy();
          });
          • Mark Stanger October 27, 2016 at 8:01 am - Reply

            Yes it does! Thanks so much for posting this solution here. I was pretty disappointed when the old functionality was broken so this is very nice to have something that works again. I’ve updated the article above with this code and some new screenshots.

  18. Stephen Mcleavey May 27, 2015 at 1:03 pm - Reply

    Does this work in UI15?

  19. Asparuh Vasilev October 13, 2015 at 9:20 am - Reply

    I am also interested if this works in UI15 with Fuji. I wasn’t able to run this workaround. Thanks in advance.

  20. Mariano Lemus February 28, 2017 at 12:25 pm - Reply

    Great post Mark, just implemented in Geneva.

    Will try it out in Istanbul.

Leave A Comment