Including a UI script in a module w/o the UI script being global

cdgaefke
Kilo Expert

I have a fairly complex set of client and server side code that will be used across multiple modules.

 

On the server side, I'm in good shape because I can put them in a Script Include and call them as needed.

On the client side, I need the equivalent, which is sort of a UI Script, but I'd rather it not be global.   The wiki references a way to include it explicitly with this code:

 

<script>


It doesn't say how/where to do that.   I read elsewhere it has to be in a UI Page, which I think means getting into Jelly, which is very new to me.   Is it possible to create a UI Page that encapsulates an existing module so that I can reference my UI Script without it being global?   If so, can someone point me to where I can learn how to do that?

 

I found this:

 

https://community.servicenow.com/thread/154072?q=ui script

 

Which is useful if the code is limited to one module.   My code is going to be across multiple modules, so I need one spot for the code.

 

Suggestions/help please?

 

Thanks.

1 ACCEPTED SOLUTION

Great to hear!   As an add-on for informational purposes only, I'll throw this out there:



Lets assume you want to include the following javascript function on a few different forms:



function sayHello(name) {


        // Fantastic aint it


        console.log('Hello, ' + name);


}



Using the Macro/Formatter method I mentioned above, you could put this function in a Macro in two ways.   The first is directly using <script> blocks:



Macro:


<?xml version="1.0" encoding="utf-8" ?>


<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">


        <script>


                  function sayHello(name) {


                            console.log('Hello, ' + name);


                  }


        </script>


</j:jelly>



The other method, as you have discovered, is using a NON-GLOBAL UI Script.   Global UI scripts are included on every page and usually adds unnecessary overhead.   Using the macro in conjunction with a non-global UI Script and a formatter, you can achieve the same exact thing.   So the following set of scripts would be functionally identical to the above:



UI Script


name: SayHello


global: false


function sayHello(name) {


        console.log('Hello, ' + name);


}



Macro:


<?xml version="1.0" encoding="utf-8" ?>


<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">


        <script>


</j:jelly>



The primary difference between the two is that the first loads a single resource - the page with content generated by the Macro.   The second loads two resources - the page with content generated by the Macro and the UI Script file loaded separately.


View solution in original post

10 REPLIES 10

Great to hear!   As an add-on for informational purposes only, I'll throw this out there:



Lets assume you want to include the following javascript function on a few different forms:



function sayHello(name) {


        // Fantastic aint it


        console.log('Hello, ' + name);


}



Using the Macro/Formatter method I mentioned above, you could put this function in a Macro in two ways.   The first is directly using <script> blocks:



Macro:


<?xml version="1.0" encoding="utf-8" ?>


<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">


        <script>


                  function sayHello(name) {


                            console.log('Hello, ' + name);


                  }


        </script>


</j:jelly>



The other method, as you have discovered, is using a NON-GLOBAL UI Script.   Global UI scripts are included on every page and usually adds unnecessary overhead.   Using the macro in conjunction with a non-global UI Script and a formatter, you can achieve the same exact thing.   So the following set of scripts would be functionally identical to the above:



UI Script


name: SayHello


global: false


function sayHello(name) {


        console.log('Hello, ' + name);


}



Macro:


<?xml version="1.0" encoding="utf-8" ?>


<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">


        <script>


</j:jelly>



The primary difference between the two is that the first loads a single resource - the page with content generated by the Macro.   The second loads two resources - the page with content generated by the Macro and the UI Script file loaded separately.


Very useful, thanks!



If I may ask... (maybe I should start a new thread).



How can the same be done with a Script Include (server side)?   I know how to reference a function within a Script Include, but I'd like the instance of that object in the Script Include to be accessible without having to instantiate it every time.



Say I have a Script Include called utils.   It has a bunch of functions in it, some of which need to access data that have been determined by other functions.   Right now I'm calling each function from a business rule (or ajax) like:



new utils().function1();



Of course since it's instantiated as new every time, another call to it can't access variables the first one populated:



new utils().function2();



Which means I have repeat code and work.



What I'm assuming I can do, I just don't know how yet, is instantiate the object once on the form load:



var myUtils = new utils();



and then from that point forward everything just reference myUtils:



myUtils.function1();


myUtils.function2();



And myUtils could have some variables, private or otherwise, that are accessible from function1 to function2:



myUtils.var1


myUtils.var2



Is such a thing possible?


Hi Charles,



The answer here is a little more complicated... it depends.   Javascript depends entirely on scope and ServiceNow runs different scripts in different scopes.



For example, lets say you insert an Incident record.   Every Incident business rule whose condition matches will run and these rules are all executed on the same scope.   Thus, if you create myUtils in a Business Rule where the Order is 100, the myUtils variable will exist in every subsequent Business Rule that runs on that record (unless you explicitly change the scope by wrapping the business rule in a function).



If you submit two Incident records, each record will have its own Business Rule scope.



From an AJAX perspective, each AJAX call exists within its own scope.   So 2 ajax calls can not share variables.



What you can do with AJAX though, is pass the context from one call to the next.   So the client would hold the variables and pass all needed values to the server.



I understand this is a complex topic, so if you are having a hard time understanding let me know and I will see if I can do a screencast to demonstrate.


Thanks for your detailed explanations.   This helps a lot.



I have a bunch of server side and client side code that will be re-used on a lot of forms.   I really want all that code to be in one place so that it doesn't have to be replicated.



I'm going to experiment with your suggestions and see what works for me.



Thanks again for your help!


Also, I'm missing how you tag code to appear separate from normal conversation.   I was expecting to see editor help somewhere, but so far I can't locate it.