The Now Platform® Washington DC release is live. Watch now!

Help
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Unable to create UI Action that calls UI Script in Fuji Scoped Application

kabyrne
Kilo Contributor

I'm in the process of migrating some existing functionality to a Fuji (patch 3) Scoped Application.

There's a UI Action that when clicked, calls a function that is defined in a UI Script.

I created the following scoped app UI Script:

var x_proj_test = x_proj_test || {};

x_proj_test.TestUiScripts = (function() {

  "use strict";

  return {

  testAlert: function(text) {

            alert(text);

  },

  type:   "TestUiScripts"

  };

})();

I then attempted to create a new UI Action, with Form Button, Active, Show update, and Client checked. The table is cmdb_ci.

In the Onclick field, I tried

TestUiScripts.testAlert('Hello')

and

x_proj_test.TestUiScripts.testAlert('Hello')

When the Onclick form loses focus, the following error is displayed beneath the field:

Your onclick must be a valid Javascript function call. Example: "onClick()"

What is the correct syntax for calling a UI Script from a UI Action from an onClick event?

1 ACCEPTED SOLUTION

coryseering
ServiceNow Employee
ServiceNow Employee

Hi Keith,



Scoped UI Scripts can't be marked as global, so they have to be deliberately loaded in a form for you to be able to use them. Additionally, for UI Actions, your onclick should correspond to a function defined in the Script field on that action.



There are a couple of ways to get your UI Script onto the page. If you have rights to create client scripts, you can use the ScriptLoader class to load the UI Script.



Screen Shot 2015-11-10 at 10.55.14 AM.PNG



The code in the onLoad script is very simple:


function onLoad() {


    //Type appropriate comment here, and begin script below


    ScriptLoader.getScripts('sn_testaction.TestUiScripts.jsdbx', function(){});


}



The ScriptLoader will fetch the UI Script, and then call the callback. In this case, we just want to fetch the script, and will let the UI Action call it the specific function we want on-demand.



Our UI Action is not very complicated- but we must define a function in the Script field for this to work.


Screen Shot 2015-11-10 at 11.00.27 AM.PNG


Again, our code is really simple. We could do extra checks to make sure our UI Script has loaded and so forth, but for demonstration purposes, this should be enough to show the principle:



function testAlert() {


              sn_testaction.TestUiScripts.testAlert();


}



You could move the loading of the UI Script into this UI Action script, but remember that it is asynchronous. If the function must run before the form submits, you need to keep track of whether the script has loaded and the callback has been called with some additional checks.



If you don't want to load the script asynchronously, then you can use a Formatter and a UI Macro to load the script. This assumes that you own a view (or can add a new Section) that you can add your formatter to. Here is that that looks like:



Screen Shot 2015-11-10 at 11.05.53 AM.PNG



The code is a really simple requires tag:


<?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:requires name="sn_testaction.TestUiScripts.jsdbx" />  


</j:jelly>



That macro is used in a Formatter:


Screen Shot 2015-11-10 at 11.07.17 AM.PNG



And the Formatter is added to the form:


Screen Shot 2015-11-10 at 11.13.54 AM.PNG



By going the UI Macro/Formatter route, you don't need to do the extra async checks, because the script is actually loaded with the form. However, it is more annoying because you need to have a Section or View that belongs to your application.



I hope that is helpful.


View solution in original post

4 REPLIES 4

Brad Tilton
ServiceNow Employee
ServiceNow Employee

If it's not a global ui script, then you have to include it on the page before it can be called. In order to do that you can use a formatter to call a macro with a reference to the uiscript and add the formatter to the form. It's pretty easy to use g:include to call the script in the macro. I know it's a bit convoluted...


Creating a Formatter - ServiceNow Wiki


coryseering
ServiceNow Employee
ServiceNow Employee

Hi Keith,



Scoped UI Scripts can't be marked as global, so they have to be deliberately loaded in a form for you to be able to use them. Additionally, for UI Actions, your onclick should correspond to a function defined in the Script field on that action.



There are a couple of ways to get your UI Script onto the page. If you have rights to create client scripts, you can use the ScriptLoader class to load the UI Script.



Screen Shot 2015-11-10 at 10.55.14 AM.PNG



The code in the onLoad script is very simple:


function onLoad() {


    //Type appropriate comment here, and begin script below


    ScriptLoader.getScripts('sn_testaction.TestUiScripts.jsdbx', function(){});


}



The ScriptLoader will fetch the UI Script, and then call the callback. In this case, we just want to fetch the script, and will let the UI Action call it the specific function we want on-demand.



Our UI Action is not very complicated- but we must define a function in the Script field for this to work.


Screen Shot 2015-11-10 at 11.00.27 AM.PNG


Again, our code is really simple. We could do extra checks to make sure our UI Script has loaded and so forth, but for demonstration purposes, this should be enough to show the principle:



function testAlert() {


              sn_testaction.TestUiScripts.testAlert();


}



You could move the loading of the UI Script into this UI Action script, but remember that it is asynchronous. If the function must run before the form submits, you need to keep track of whether the script has loaded and the callback has been called with some additional checks.



If you don't want to load the script asynchronously, then you can use a Formatter and a UI Macro to load the script. This assumes that you own a view (or can add a new Section) that you can add your formatter to. Here is that that looks like:



Screen Shot 2015-11-10 at 11.05.53 AM.PNG



The code is a really simple requires tag:


<?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:requires name="sn_testaction.TestUiScripts.jsdbx" />  


</j:jelly>



That macro is used in a Formatter:


Screen Shot 2015-11-10 at 11.07.17 AM.PNG



And the Formatter is added to the form:


Screen Shot 2015-11-10 at 11.13.54 AM.PNG



By going the UI Macro/Formatter route, you don't need to do the extra async checks, because the script is actually loaded with the form. However, it is more annoying because you need to have a Section or View that belongs to your application.



I hope that is helpful.


Cory (and Brad) thanks for your help.



I didn't want to create a custom view for the form, so I used the ScriptLoader method.



Like Brad said, this is a bit convoluted. I would expect a scoped UI Script to be readily available anywhere within the scoped app without having to explicitly load it on the page.   Both methods seem like workarounds, and I hope that this is remedied in future releases.


So I ran full tilt into this problem as well.   Thanks Cory, for documenting a completely undocumented feature!   This needs to be trumpeted; so I will do so!   🙂



My solution needed to do everything in the onLoad so I went ahead and expanded Cory's solution out a bit.



function onLoad() {


      ScriptLoader.getScripts('sn_testaction.TestUiScripts.jsdbx', followup);



      function followup(result) {


              jslog('made it');


              jslog(sn_testaction.TestUiScripts.testAlert());


      }


}