logo

NJP

How to Force a Cache Update for UI Scripts

Import · May 10, 2018 · article

Consider the following situation. You have been tasked with developing a user experience in ServiceNow that is a bit outside of the normal UI functionality. You decide to develop a UI Script to handle the requirements. Maybe you are consolidating logic that exists in Client Scripts into one function that is available on all forms. You develop and roll out the initial version which looks something like this:

function doTheStuff(g_form, fldName){
    var ga = new GlideAjax('DoStuffHelper');
    ga.addParam('sysparm_name','getTheStuff');
    ga.getXML(ParseUserData);
    function ParseUserData(response) {
        //parse JSON answer from the response
        var answer = response.responseXML.documentElement.getAttribute("answer");
        if(debugEnabled){
            console.log('doTheStuff: answer ---> ' + answer);
        }
        var ans_obj = JSON.parse(answer);
                if(debugEnabled){
            console.log('doTheStuff: stuff ---> ' + answer.stuff);
        }
        g_form.setValue(fldName, answer.stuff);
    }
}

You save, test, and roll to production. All is well. However, a bug is soon discovered and you need to troubleshoot it. You try calling the function with debug on, but it doesn't work. You research a bit and find that you forgot to declare the parameter for the function. So you fix the issue:

function doTheStuff(g_form, fldName, debugEnabled){
    var ga = new GlideAjax('DoStuffHelper');
    ga.addParam('sysparm_name','getTheStuff');
    ga.getXML(ParseUserData);
    function ParseUserData(response) {
        //parse JSON answer from the response
        var answer = response.responseXML.documentElement.getAttribute("answer");
        if(debugEnabled){
            console.log('doTheStuff: answer ---> ' + answer);
        }
        var ans_obj = JSON.parse(answer);
                if(debugEnabled){
            console.log('doTheStuff: stuff ---> ' + answer.stuff);
        }
        g_form.setValue(fldName, answer.stuff);
    }
}

You test your work thoroughly this time, but it keeps failing. The debug statements simply don't work. After much research, you discover that, although you updated the UI Script, the old version is still being loaded into the form. You try everything you know to no avail, but nothing fixes the issue (cache.do, multiple saves, toggling active, etc...). You become frustrated at this point and flush your browser cache, and voila! It's fixed. But your developers and customers that have previously used the functionality still have the issue. So you turn to workarounds.

To Flush or Not to Flush?

  • One workaround, albeit not a great one, is to have the affected users clear their browser cache (cookies). This is a terrible experience for the customer and basically just kicks the can down the road to the next update.
  • Another workaround you found is to rename the script. This is a better user experience for the customer, but frustrating for developers. You now have to track down every reference to the script and update it. Code search from studio makes this easier, but its still frustrating.

...and I do mean workaround. This is by no means to be considered a fix. The secret is in the browser cookies. We know that many plugins contain UI Scripts. These applications are updated from time to time (including the UI Scripts) and that works fine. What's the difference? Activating a plugin triggers a forced cache update for the users' browsers. The difference is that updating a UI Script doesn't trigger this. Not even a cache.do triggers it. It simply forces the server to update cache. Installing/updating plugins seems to be the sole trigger that forces the browser to ask the server for updated cache for UI Scripts.

So how do you force this cache update?

The check for building new cache in this instance is controlled by a cookie on the client browser that ServiceNow creates. The cookie is controlled by a property in ServiceNow called glide.lastupdate. To force this update to happen, all you have to do is set that property to a newer dateTime than it currently is (e.g. the current timestamp). You can do this manually, but that can also be a form of kicking the can down the road. You will be back here soon. At Walmart, we wrote a Business Rule to update this whenever a ui_script is updated:

(function executeRule(current, previous /*null when async*/) {

    var gdt = new Date();
    var gdtString = gdt.toString().replace(/ /g,'_').replace(/:/g,'_');
    var timeZone = gdtString.replace(/.*[(](.*)[)].*/,'$1');
    var dateFinal = gdtString.slice(0,25) + timeZone;
    gs.addInfoMessage('System Property glide.lastplugin updated. ' + 
                      'Please be sure to push this updated property with the rest of your code. ' +
                      'This will ensure that the JavaScript cache is updated on the client browser.');
    gs.setProperty('glide.lastplugin', dateFinal);

})(current, previous);

I have attached an export of this business rule for any who are interested. The info message reminds developers to ensure this updated property is included in their update set, since business rules are not triggered on update commit.

View original source

https://www.servicenow.com/community/developer-articles/how-to-force-a-cache-update-for-ui-scripts/ta-p/2307365