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…
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:
function runClientCode(){
if( == false){
return false; //Abort submission
}
//Call the UI Action and skip the 'onclick' function
gsftSubmit(null, g_form.getFormElement(), ''); //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. = ;
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
Name: Reopen Incident
Action name: reopen_incident
Client: True
Form button: True
Onclick: reopen();
Condition: current.state == 6
Script:
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);
}
Ingenious – and somewhat simpler than the way the Incident Resolution best practice plugin does the same sort of thing to make close notes mandatory on resolution or closure. The (BP) Close Mandatory on Close or Resolve client script is quite dastardly.
Another reason why I have stopped going to the official SN documentation sites and make the GURU my documentation site of choice. You come up with clever solutions that every admin could use.
This is really cool!
So cool, makes it so much easier/cleaner to do confirm() before executing server-side code. Definitely something I will use all the time, thanks!
Working perfectly…. thnks a lot for sharing this knowledge !!
Hi,
I got a little problem with that script. Indeed, it works perfectly with buttons in the bottom of the form.
But, my buttons in the title bar does not work. They launch the script client side but the ‘gsftSubmit’ action is inactive. Maybe because the button has no id (when selecting client for the UI Action, the action name is not associated to the id of the html button).
Have you experienced that before?
Regards,
Thak
I haven’t seen the problem you describe before.
For your information, I have solved my problem.
As I expected, the problem was not coming from the code of that post. The point was coming from the List V2 plugin.
I was using a embedded list in my form, and as that list was empty, it was raising the following error in my FF javascript console :
this.tableElementDOM.rows[0] is undefined.
And that error was in conflict with the gsftSubmit action, so that the form was not able to be submitted.
Thanks for your quick answer.
P.S : I raised that bug to SNC
Wow, that’s a strange one. Thanks for digging in and figuring it out. 🙂
Cool, got it working with a confirmation before undertaking the actual action. I’m using gs.addInfoMessage do add an info/error message at the top of the screen to say the action was successful or not, but the client would prefer an alert window instead. Would that be possible?? Can’t see how – I tried adding an alert after the call to the server side code, but it actually pops up before the code is run.
I don’t think that’s possible if you’re trying to add the message after the server-side code executes. You’ll need to use addInfoMessage for that final confirmation.
That’s what I figured. I used HTML to change the message to be pretty obvious and it takes up quite a bit of room at the top of the screen. If the user does not see it, then there’s a problem.
Thanks
I want to use this script, but I keep getting an error that g_form is undefined. Any ideas why?
gsftSubmit(null, g_form.getFormElement(), ”);
The error suggests that you’re trying to run the script in a place other than a regular form. You won’t be able to get it to work from a UI page or a list for example. If you can set up a test on demo and reproduce there I can take a look.
It works fine, once you close and reopen the browser. That was all it took.
Fantastic ! Too Good.Thanks a lot Mark !
Thanks Mark!!
Hi Mark,
I’m trying to use this in conjunction with a dialog.render in the client side, before updating the record on the server side. The problem I’m having is that the gsftSubmit(null, g_form.getFormElement(), ‘cancel_request’); line is actioned as soon as the dialog box is rendered so the user doesn’t get the chance to fill in the dialog box. Is there a way I can make it wait for the OK button on the rendered dialog to be pressed before proceeding to the gsftSubmit ?
I’m not sure about the specifics of your setup, but it sounds like you need to have a ‘return false;’ line in your client-side piece of the UI action so that the bottom form doesn’t submit.
Thanks Mark – unfortunately then the script stops so the server side part is never executed. I need it to still execute the server side part of the UI Action but only after the OK button on the dialog form is pressed.
Hi Mark – can you tell me if it’s possible to pass a variable (using var xxx = prompt(‘ask user a question’)) from within the client function to the server function (so I can push the variable value into multiple other records)? I tried declaring as a global variable but the server side can’t see the variable. Thanks
The only way I can think of to do that would be to set a value in a hidden field in your client script. Maybe you could set up a hidden field to store parameters temporarily just for that purpose.
Setting the value in a hidden field (and saving the form) will not update the record with that value. Only the fields that are visible.
Ruth’s idea below about using GlideAjax may be the only way to go.
Oh wait, no that was inaccurate. A hidden field will work, it just needs to be a part of the view.
Hi Mark, can you tell me if the button of “Update selected” or “Update all” could be edited to generate a alert/waring and then perform updates.
The requirement is to generate a pop-up after user clicks on “update button” not before.
What I have noticed is the ‘update’ button has server side scripts which performs the updates and any changes done to the button(ui actions) through client callable just executes the client script but doesn’t execute the server scripts. I have even tried to create a new button and calling the update button using gstfsubmit (action_name of update button) it works fine but we need to hide that ‘update button’ but when I go back to ‘update button’ to hide it from my update view by using ‘ui action visibility’ it stops working.
I haven’t ever been able to find anything to control the behavior when using ‘Update selected’ or ‘Update all’. Unfortunately I think you’re stuck with the standard behavior there.
Hi Mark,
Bit of a slow response but with regard to Russ’s point about passing a variable to the server side: I have done this by using a GlideAjax call (which can be passed a variable) from the client side bit and having the server side bit just do what is left.
Seems to work OK and gets rid of need for extra hidden field
Nice! Thanks for the good idea Ruth!
ok, I can officially confirm I am a scripting dumbo. I have tried various versions of this along with other articles that have similar functions and I cannot get it working. all I am trying to do is “confirm” that a user wants to create a problem record from the incident record, ie when they select the Create Problem UI Action on the Incident it pops up the confirm dialog, if Yes it continues with creating the PRB, if cancel it stays on the Incident. sounds simple right? not for this dumbo!
Scott, I just wrote an example script that should give you the exact structure you’re looking for. You’ll just have to convert it over to your UI action. Check out the update script here with the ‘Confirm’ message.
https://servicenowguru.wpengine.com/system-definition/remove-activity-log-journal-entries/
THANKS MARK! You are a genius.. Your script made sense to me the more I read it. I think where I was falling over in my previous scripts was “NOT” calling, as I have shown in script below the “//server-side function” after doing the check for browser errors, simple mistake I was making and using the modified version (below) of your script woke me up to that.
This is the script I ended up with and it works a treat.
Action name: create_problem
Client: True
Form button: True
Onclick: runClientCode();
function runClientCode(){
if(confirm('Are you sure you want to create a Problem Record?\n\nTHIS ACTION CANNOT BE UNDONE!')){
//Call the UI Action and skip the 'onclick' function
gsftSubmit(null, g_form.getFormElement(), 'create_problem'); //MUST call the 'Action name' set in this UI Action
}
else{
return false;
}
}
//Code that runs without 'onclick'
//Ensure call to server-side function with no browser errors
if(typeof window == 'undefined'){
CreateProblem();
}
//server-side function
function CreateProblem(){
var prob = new GlideRecord("problem");
prob.category = current.category;
prob.sub_category = current.subcategory;
prob.impact = current.impact;
prob.urgency = current.urgency;
prob.priority = current.priority;
prob.assignment_group = current.assignment_group;
prob.short_description = current.short_description;
prob.description = current.description;
prob.cmdb_ci = current.cmdb_ci;
prob.u_business_service = current.u_business_service;
prob.priority = current.priority;
var sysID = prob.insert();
current.problem_id = sysID;
var mySysID = current.update();
gs.addInfoMessage("Problem " + prob.number + " created");
action.setRedirectURL(prob);
action.setReturnURL(current);
}
That’s great! I’m glad you got it working.
Hi,
just a heads up for everyone:
There is a short version available for
.
Instead of using
you can also use
.
There is only one issue one could trip over: It only works when the UI Action is a form button. When one is using a form link or form context menu type, the
portion is not working.
But I also have a question:
Are the any other differences or known issues than the one I outlined above?
We are using a similar one for Cancel change request functionality, however this seems to be working intermittently, that is most of the times the cancellation happens as expected. But in some cases, state is not getting set to Cancelled though we get the configured message ‘Change Cancelled’. Also Active is changing to false. Any idea what could be causing this intermittent behavior?
{
var ans = confirm('Are you sure you wish to Cancel this Change? If cancelled, this record cannot be reused.');
if(ans == false)
{
return false;
}
else
{
gsftSubmit(null, g_form.getFormElement(), 'cancel_change');
}
}
if(typeof window == 'undefined')
{
cancelchange();
}
function cancelchange()
{
current.state = '4';
current.update();
gs.addInfoMessage('Change Cancelled');
action.setRedirectURL(current);
}
If it’s working most of the time, my guess is that you’ve got a business rule or some other script that is setting the state value to something different. I would review the business rules in the system (along with tickets that haven’t closed correctly) to see if there are any commonalities between them.
Also make sure that the action name of your UI Action is not the same as the onClick function name.
If both are the same you get some really funny and weird behavior. I always add a “_” to the action name – e.g.: cancel_change
Looking at your script that seems to be fine (cancelClient vs cancel_change). But you are not cancelling the workflow unless your workflow is having a parallel loop which keeps checking for the state of the change request, which then ends the workflow once it is fulfilled and cancels all active tasks.
Check here for some scripting ideas: https://servicenowguru.wpengine.com/scripting/business-rules-scripting/canceling-executing-workflows-task-closure/
Great explanation. Have just learned the same on the Scripting Cource, but we didn’t get this great explanation why and how it’s actually working.
You’re welcome, I’m glad you found it useful! I hope they’re still telling people in the course where they got the idea from :).
This is a great tutorial. However, is there a way to do this on Mobile? Because g_form.getFormElement() is a deprecated method.
I’m not sure what the mobile UI does to reference the form, but you might be able to get away with simply hard-coding the form ID name in place of ‘g_form.getFormElement’ like this…
gsftSubmit(null, ‘incident.do’, ‘‘);
Thanks for the quick response Mark. Unfortunately it looks as though ServiceNow mobile does not support Onclick within ui actions, so it looks like I’m going to need to find an alternative solution all together.
Hi Mark, this tutorial has helped me quite a bit. I was wondering though, are you aware if something has changed in Fuji to make it so the condition – if(typeof window == ‘undefined’) – will no longer get called? This appears to be occurring in my Dev instance after it was upgraded from Eureka.
Thank you Brian. There’s nothing that has changed in Fuji to cause the issue you’re seeing as far as I’m aware. The ServiceNow demo systems ship with this kind of code in the standard ‘Resolve Incident’ UI action and it works just fine there.
Thanks for the quick reply and pointing me in the right direction. I wasn’t quite sure how to tell if the function was getting called properly or not. Turns out the issue was that my UI Action’s “Action Name” had the same name as another UI action. Which for some reason wasn’t a problem in Eureka, but I’m glad they have that standard now.
Excellent work. It works like charm but for my situation ServiceNow glitch comes in i have below situation.
We have Form A, where we have new button to open new form which take it to anther form where user fills data multiple times and we wanted to implement Go Back Functionality.
I tried to implement my scenario using work-around above it works perfect but if user have not entered any data into mandatory fields then click on Go Back button did not take to to the parent form.
However i would like to share with you guys the solution for may be you or any one else face this situation.
– I made GO Back button on the form
– Enabled button as Client – as described in this page original post
– paste this -> parent.history.back(); in onClick box
no need to put any scripts in script box
It worked like cheers now going back was so easy. 🙂
And least but not last thanks to Servicenow guru who posted this page it gave me starting point to make a button client and server side.
I’m a new ServiceNow admin and I’m definitely going to make sure tell people about this site! Thank you for your help, first off.
Is the below code referencing a business rule? Does there have to be an accompanying business rule called “runBusRuleCode”?
One of the biggest challenges I am having right now is the interconnectedness between client and server components. UI Actions and business rules, client scripts, and script includes, etc. How do all of these pieces fit together? And, where do I put this function reference in this other piece of ServiceNow.
//Server-side function
function runBusRuleCode(){
current. = ;
current.update();
gs.addInfoMessage(‘You did it!’);
action.setRedirectURL(current);
}
@William, the purpose of this solution is to avoid having to have a separate UI action, client script, and/or business rule. The entire piece of code from this solution should go in the UI action ‘Script’ field. The call to the ‘runBusRuleCode’ function is made within the UI action Script itself.
Hello, is it possible to use implement somthing ismilar, with glidedialogwindow on a ui action (for example: cancel) and after in the ui page a client script like the following:
function prCancel(){
GlideDialogWindow.get().destroy(); //Close the dialog window
reloadWindow(window); //Refresh the form
}
function cancelprocess(){
current.state = 13;
current.stage = ‘Request Cancelled’;
current.update();
var osmq_tsk = new GlideRecord(‘u_osmq_task’);
osmq_tsk.addQuery(‘parent’, current.sys_id);
osmq_tsk.query();
while (osmq_tsk.next()) {
osmq_tsk.work_notes = “Request item ” + current.number + ” was cancelled by associate. Please, Cancel task in OSMQ.”;
osmq_tsk.update();
}
}
It’s not because once the UI action is clicked it’s done. The dialog would then be running on its own and wouldn’t have anything to do with the UI action that called it. You would want to set up your UI page with the necessary back-end code included in ‘cancelProcess’ instead. That’s a bit more complex to explain the specifics of, but that’s where it would need to be initiated from.
I am taking advantage of your client & server code in one UI action but am running into issues yet again. Now when I launch the form it just closes immediately. Is there something in the code below that would cause it to do that?
//Client-side ‘onclick’ function
function newCredentialForm(){
var tableName = g_form.getTableName();
var sysID = g_form.getUniqueValue();
//Call the UI Action and skip the ‘onclick’ function
gsftSubmit(null, g_form.getFormElement(), ‘newCredentialForm’); //MUST call the ‘Action name’ set in this UI Action
//Create and open the dialog form
var dialog = new GlideDialogForm(‘Create Temporary Credential’, ‘u_temp_credential’); //callbackFunct no longer necessary
dialog.setSysID(-1); //Pass in sys_id to edit existing record, -1 to create new record
dialog.addParm(‘sysparm_view’, ‘credential_view’); //Use the Credential view of the form
dialog.addParm(‘sysparm_form_only’, ‘true’); //Remove related lists
dialog.render();//Open the dialog window
}
/*
//Get the values from the Incident Table so we can write-back to it. Done in server side code now.
var comments = g_form.getValue(‘comments’);
var short_description = g_form.getValue(‘short_description’);
var work_notes = g_form.getValue(‘work_notes’);
*/
//Code that runs without ‘onclick’. Ensures call to server-side function with no browser errors
if(typeof window == ‘undefined’)
runServer_SideCode();
//Server-side function – not a business rule
function runServer_SideCode(){
//returning data from the temp cred form
var obj = {};
obj.application = current.u_application;
obj.username = curent.u_for_user.user_name; //u_temp_credential.u_for_user => sys_user (table).user_name (userID field)
obj.password = current.u_temp_password;
//testing the dot walking for the username
alert(obj.username);
var utils = new CatalogUTILS();
var uri = utils.setTempCredential(current.caller_id,obj.application,obj.username,obj.password,current.sys_id);
current.comments = ‘to access your new credentials blah blah blah https://‘ + gs.getProperty(‘instance_name’) + uri;
gs.addInfoMessage(‘You did it, scripting super-star!’);
action.setRedirectURL(current);
}
Hi William,
Did you manage to get any result after running your script?
I noticed that you are trying to run ‘alert’ in server-side, i,e alert(obj.username); alert is run in the client side – you need to use gs.addInfoMessage(obj.username) to get the output.
Regards,
Samiul
Does this works even for a mobile UI action
No. Mobile UI is severely limited in regards to scripting support.
Hi Mark, I have attempted to use the script provided but it is not working for us, in fact button is no longer showing anymore. I am new to scripting so I think that is the issue for me. Purpose is we have created a new type of task (RDTASK) and the users want a Close Task button in the header. Upon closure there are 5 fields that need to be mandatory before task can be closed. Right now I just have one in the code as I am just trying to get that to work. I am at a loss on how to make this work and would love for some assistance.
Hi Jennifer. Questions like these are probably best asked on the ServiceNow community site. Having said that, your button isn’t appearing because the ‘Condition’ value you have can’t ever evaluate to true (which displays the button).
A condition of ‘current.state == 3 && current.state == 4’ is impossible since the state can’t be both 3 and 4 at the same time. I think instead of ‘and’ you want an ‘or’. Something like this for your condition should work better.
current.state == 3 || current.state == 4
Does this leave the server script open for user manipulation at that point, since it’s now on the client side? Or is the server-version of the code only called on the server side when the button is clicked?
Worked perfectly! Thank you, Mark, for providing such great solutions to common problems. Keep up the great work!
Hi All,
How to make this work on a UI action on related list ? I know that it says explicitly in the beginning that it is for only ‘Form button/Form Context Menu/Form Link’.
I want users to be able to only select one of the related list records, and show them an alert when they select multiple and hit UI action.
Client side of UI action : works fine, and counts the number of records selected and displays alert.
Server Side : loses track of the ‘current’ record it was working on.
After gsftsubmit, record of the main form ( on which related list was) becomes the current record.
Anyone knows a way around that ?