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

Help
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
Michael Ritchie
ServiceNow Employee
ServiceNow Employee

A common use case in ServiceNow is to have data driven forms that fill themselves in based input from other fields.   For example after selecting a user, fill in location fields, department fields, etc based upon the selected user's information.   This is typically accomplished using a ServiceNow client script using the out of the box g_form.getReference() function.   While this function is useful, it has its shortcomings including the ability to "dot-walk" into the referenced record's references.   For example if your form needs to display the user's location information like street, city, state, and zip, the getReference() function cannot "dot-walk" into the user's location record to gather these attributes because "location" is a reference on the user record.   The typical answer to solving this requirement is GlideAjax that includes a client side call to a server side script to gather the details and then pass it back to the client.   GlideAjax is quite useful and very powerful, however it can be difficult to setup since it requires somewhat complicated step by step instructions to make it work.

 

On top of detailed step by step instructions, GlideAjax setup is different between the desktop UI and the service portal UI.   If you are not careful, you can have GlideAjax "sprawl" where you have many script include records for point solutions.   There have been several ideas on the ServiceNow Community discussing how to solve this problem such as goranlundqvist's "Lets make GlideAjax a little more dynamic" and tim.harris's "Standardizing GlideAjax Calls to Prevent Script Include Sprawl".   Those posts have been very useful in getting people going with GlideAjax.

 

"getReferenceAdvanced" is an easier solution that is a simple function call form a client script.   In this function call, you can specify one or more attributes that you want from the referenced record including "dot-walk" attributes.   A global UI script enables this functionality on any form or service portal in your instance.

 

Note: The mobile app and UI do not support UI Scripts so this solution will not work for the app store mobile app or the mobile browser ($m.do) at this time.

 

Another challenge with GlideAjax is queries are happening server side by a system process and if you are not careful you may be exposing more data than the logged in user is supposed to have access to.   You will find many comments on the Community about this.   This solution solves that issue by using GlideRecordSecure instead of GlideRecord thus enforcing ACLs.

 

The Use Case:

I searched the Community for common use cases where the getReference function wasn't working and GlideAjax was proposed.   One customer had a form with a location reference field and the location's street, city, state, and zip needed to be filled into separate fields on the form.   Another user needed to fill in separate fields for email, department name, and department cost center's code after filling in the caller.   These are both excellent examples of where the coding to accomplish this use case can be complicated.

 

The getReferenceAdvanced solution is much simpler.   In testing, I setup a catalog item form that prompts for a "Requested For" user which is pretty typically across any type of task/case intake form.   Once the user is selected details from the user's profile are filled in below the Requested For including location and department information.   Below is a screenshot of this form indicating the source of data when the getReferenceAdvanced function runs:

find_real_file.png

 

The Code:

This solution involves several components that is available for download from ServiceNow Share: getReferenceAdvanced Client Script Function, a GlideAjax Alternative.   Download the update set from Share, load/preview/commit it, and then you can leverage it in your client scripts.

 

  • UI Script called getReferenceAdvanced: The getReferenceAdvanced* functions are part of a global UI script which make it available to a client script on any form in any scope in your ServiceNow instance.   This client script validates the data passed to it and then makes a REST web service call back to the instance to gather data.
    • For security purposes the user's security token is passed to the REST web service which prevents unauthorized access to instance data as well as enforcing ACL's for the logged in user.
  • Scripted REST API called getReferenceAdvanced: The getReferenceAdvanced UI Script passes data to this Scripted REST API that then gathers the data requested and passes it back to the client script.   A Scripted REST API was used instead of GlideAjax because calling a web service behaves the same in the Desktop UI and the Service Portal UI which simplifies the code.   This web service requires authentication for security purposes and the user's session token is used for the authentication.

 

The Setup:

The desktop UI and the Service Portal UI behave a little differently so two different function calls are provided for the two different UIs:

  • Desktop:
    • getReferenceAdvancedDesktop("REFERENCE-FIELD-NAME", "SEMICOLON-SEPARATED-LIST-OF-FIELDS-YOU-WANT");
  • Service Portal:
    • getReferenceAdvancedPortal(g_form, "REFERENCE-FIELD-NAME", "SEMICOLON-SEPARATED-LIST-OF-FIELDS-YOU-WANT");
    • Notice the getReferenceAdvancedPortal function has a third input of "g_form".   This is a required input and is basically an object containing all the details about the form being displayed.   For some reason the g_form object is not passed to the UI Script from the Service Portal like it is with the desktop UI so it is required that it is passed.

 

The getReferenceAdvancedDesktop and getReferenceAdvancedPortal return a JSON object (name/value pair) with the field requested and its value from the reference record.   Because the data is returned in an Object, all the "dots" that were passed into the function are changed to underscores so that you can call out each data element individually.   For example the value of "location.street" is stored in the "location_street" attribute within the returned object.   This data can then be combined with the g_form.setValue() function to set values on the form.

 

OK so this sound complicated, how is this simpler and how do I use it?!!   Considering use case mentioned above and the test form screenshot, the following code is used in an onChange client script to fill in the form.

 

Desktop Script:

function onChange(control, oldValue, newValue, isLoading) {

      if (isLoading || newValue == '') {

              return;

      }

     

      var reqFor = getReferenceAdvancedDesktop("requested_for", "location.street;location.city;location.state;location.zip;department;department.dept_head;department.cost_center.code");

      g_form.setValue("street", reqFor.location_street);

      g_form.setValue("city", reqFor.location_city);

      g_form.setValue("state", reqFor.location_state);

      g_form.setValue("department", reqFor.department);

      g_form.setValue("department_manager", reqFor.department_dept_head);

      g_form.setValue("department_cc_code", reqFor.department_cost_center_code);      

}

 

Service Portal Script:

function onChange(control, oldValue, newValue, isLoading) {

      if (isLoading || newValue == '') {

              return;

      }

     

      var reqFor = getReferenceAdvancedPortal(g_form, "requested_for", "location.street;location.city;location.state;location.zip;department;department.dept_head;department.cost_center.code");

      g_form.setValue("street", reqFor.location_street);

      g_form.setValue("city", reqFor.location_city);

      g_form.setValue("state", reqFor.location_state);

      g_form.setValue("department", reqFor.department);

      g_form.setValue("department_manager", reqFor.department_dept_head);

      g_form.setValue("department_cc_code", reqFor.department_cost_center_code);

     

}

 

As you can see in the example script, instead of making multiple getReference calls, only one was made with a semicolon separated list of the attributes needed from the Requested For's user record.   This has a performance benefit because less database calls are being made as well as huge user experience increase because the user is waiting less time for the form data to be filled in.

 

Notice the getReferenceAdvanced* call is set to the "answer" variable that is then used for the setValue() function calls.   Again each of the attributes requested using "dot-walking" are updated with an underscore but using the same name.   If only one attribute is needed from the reference record, either of the following scripts will work.   In this example the user's department's manager (dept_head) is needed:

g_form.setValue("department_manager", getReferenceAdvancedDesktop("requested_for", "department.dept_head").department_dept_head);

 

--OR--

 

var reqFor = getReferenceAdvancedDesktop("requested_for", "department.dept_head");

g_form.setValue("department_manager", reqFor.department_dept_head);

 

In order to use the getReferenceAdvanced UI script working in your Service Portal(s), you will need to configure your Service Portal Theme.   As specified by James.Neale in this Community Article:

  1. Navigate to Service Portal\Themes and select your theme.   If you are using the OOB Service Portal, click Stock.
  2. Scroll down to the "JS Includes" Related List and click New.
  3. Set the name to getReferenceAdvanced or whatever makese sense to you, Source should be set to UI Script and then select the getReferenceAdvanced UI Script and click Submit.
  4. Repeat steps 1-3 if you have multiple Service Portals that need this capability.

 

Error Handling/Troubleshooting:

To make this as painless as possible to setup, error handling has been set in the getReferenceAdvanced UI Script.   These alert popup messages will appear when testing your client script:

  • If you call getReferenceAdvanced* in your client script and forget to specify a reference field, you will receive the following message: The getReferenceAdvanced() function requires a field to be passed.
  • If you call getReferenceAdvanced* in your client script and is NOT a reference field, you will receive the following message: The getReferenceAdvanced() function only works with reference fields.   The field specified is not a reference field.
  • If you call getReferenceAdvancedPortal in your client script and you don't pass the g_form object, you will receive the following message: The getReferenceAdvanced() function requires the g_form object to be passed from the Service Portal.   Please pass that as the first parameter.
    • Remember that Service Portal client scripts (Type is Mobile/Service Portal) require you to use the getReferenceAdvancedPortal() function and the first parameter needs to be g_form as shown on the example scripts above.
  • If you call getReferenceAdvanced* in your client script and you don't pass the fields needed from the reference record, you will receive the following message: The getReferenceAdvanced() function requires a string of field(s) you wish to return.
  • If you call getReferenceAdvanced* in your client script and the field passed does not have a value, you will receive the following message: The getReferenceAdvanced() function requires the field 'FIELD-NAME' to have a value.
15 Comments