M

ost Service-now administrators and consultants know how to configure and use UI Actions. UI Actions are UI elements that can show up on a form or a list as a button, link, or context menu. When these UI elements are clicked they execute some JavaScript. Most of the time UI Actions are used to perform some server-side update to a record or records. In other cases, you can use the ‘Client’ checkbox on the UI Action record to execute some client-side JavaScript (including checking for mandatory fields).
But what if you need to do both? The classic case is when you want to click a button to make an update to a record, but only if the user has provided the correct input first. An example would be a ‘Reopen Incident’ button that changes the state on an incident record from ‘Resolved’ to ‘Active’. Usually you want to require the user to provide some sort of comment or additional information explaining why they are reopening the ticket. The problem is that you don’t always want the ‘Comments’ field to be mandatory so the validation needs to happen at the time the ‘Reopen Incident’ button gets clicked. Validation of mandatory fields needs to happen client-side but the update to your record needs to happen server-side. How can you accomplish both of these things with a single UI Action? This article shows you how.



The basic format for using a Client Script and Business Rule in the same UI Action looks something like this…

UI Action Template
Name: -Button Name-
Action name: -button_action_name- (Should be unique per button on form and gets called from the UI Action script)
Client: True (MUST be checked)
Form button/Form Context Menu/Form Link: (UI Action must be one of these ‘Form’ types)
Onclick: -runClientCode();- (Points to the function in your script that should be run when the UI Action gets clicked)
Script:

//Client-side 'onclick' function
function runClientCode(){
   if(<CONDITION_TO_VALIDATE> == false){
      return false;  //Abort submission
   }
   //Call the UI Action and skip the 'onclick' function
   gsftSubmit(null, g_form.getFormElement(), '<button_action_name>'); //MUST call the 'Action name' set in this UI Action
}

//Code that runs without 'onclick'
//Ensure call to server-side function with no browser errors
if(typeof window == 'undefined')
   runBusRuleCode();

//Server-side function
function runBusRuleCode(){
   current.<field_name> = <value>;
   current.update();
   gs.addInfoMessage('You did it!');
   action.setRedirectURL(current);
}

So why does this work? I had to go to a Service-now developer to find out. The reason is that UI Actions can run scripts at two different times. The first time is when the UI Action gets clicked. When you define a ‘Client’ UI Action you also give that UI Action the name of a function in your ‘Script’ field to execute. This function has to be called explicitly (through the ‘onclick’ event) or it doesn’t run at all.
The second time is on the way to the server. This is how any UI Action without the ‘Client’ checkbox selected gets run. On the way to the server the entire UI Action script gets executed regardless of whether or not the ‘Client’ checkbox is checked. What this means is that any script you include in your UI Action that isn’t enclosed in a function will be run on the way to the server. The script above takes advantage of this fact by making a specific call to the ‘Client’ function, performing client-side validation, and then the UI Action calls itself if the client-side validation passes.
When the UI Action calls itself it bypasses the ‘onclick’ function because the button didn’t get clicked the second time. So the script continues to the first point where there is something to execute. At that point you can call your Server-side function! The only thing you need to be careful of is that you only call the Server-side function if the script isn’t running in the client anymore. That’s what the check in the middle does…and eliminates any browser errors saying that ‘current’ (or any other Server-side function or object) isn’t defined.

Here is a solution I’ve used in the past to give users the ability to reopen an incident record. The solution uses a UI Action button to check if the ‘Comments’ field has been filled in (this is the ‘Client-side’ portion). If the validation passes, then the incident record gets updated.

Reopen Incident UI Action

Note that this script uses the ‘State’ field rather than the ‘Incident State’ field. In my opinion, it is much better to consolidate all of your state fields into one using the ‘State’ field at the task level as described here.

Name: Reopen Incident
Action name: reopen_incident
Client: True
Form button: True
Onclick: reopen();
Condition: current.state == 6
Script:

//Client-side 'onclick' function
function reopen(){
   if(g_form.getValue('comments') == ''){
      //Remove any existing field message, set comments mandatory, and show a new field message
      g_form.hideFieldMsg('comments');
      g_form.setMandatory('comments', true);
      g_form.showFieldMsg('comments','Comments are mandatory when reopening an Incident.','error');
      return false;  //Abort submission
   }
   //Call the UI Action and skip the 'onclick' function
   gsftSubmit(null, g_form.getFormElement(), 'reopen_incident'); //MUST call the 'Action name' set in this UI Action
}

//Code that runs without 'onclick'
//Ensure call to server-side function with no browser errors
if(typeof window == 'undefined')
   serverReopen();

function serverReopen(){
   //Set the 'State' to 'Active', update and reload the record
   current.state = 2;
   current.update();
   gs.addInfoMessage('Incident ' + current.number + ' reopened.');
   action.setRedirectURL(current);
}