TUESDAY, FEBRUARY 07, 2012

Client & Server Code in One UI Action

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);
}

Comments

Posted On
Aug 31, 2010
Posted By
Richard Huss

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.

Posted On
Aug 31, 2010
Posted By
Ron Methias

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!

Posted On
Sep 08, 2010
Posted By
Joe Watts

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!

Posted On
Sep 14, 2010
Posted By
Dhanraj P

Working perfectly…. thnks a lot for sharing this knowledge !!

Posted On
Mar 07, 2011
Posted By
Thak

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

Posted On
Mar 07, 2011
Posted By
Mark Stanger

I haven’t seen the problem you describe before.

Posted On
Mar 13, 2011
Posted By
Thak

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

Posted On
Mar 14, 2011
Posted By
Mark Stanger

Wow, that’s a strange one. Thanks for digging in and figuring it out. :)

Posted On
Mar 16, 2011
Posted By
Jim Coyne

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.

Posted On
Mar 16, 2011
Posted By
Mark Stanger

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.

Posted On
Mar 16, 2011
Posted By
Jim Coyne

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

Posted On
Apr 04, 2011
Posted By
John Gilaspy

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(), ”);

Posted On
Apr 04, 2011
Posted By
Mark Stanger

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.

Posted On
Apr 04, 2011
Posted By
John Gilaspy

It works fine, once you close and reopen the browser. That was all it took.

Posted On
May 25, 2011
Posted By
Abhiram Bharadwaj

Fantastic ! Too Good.Thanks a lot Mark !

Posted On
Jul 23, 2011
Posted By
Dhanraj Poojari

Thanks Mark!!

Posted On
Aug 17, 2011
Posted By
Russ Hart

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 ?

Posted On
Aug 17, 2011
Posted By
Mark Stanger

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.

Posted On
Aug 17, 2011
Posted By
Russ Hart

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.

Leave a Reply


Notify me of followup comments via e-mail. You can also subscribe without commenting.

Latest Comments

  • Mark Stanger: This linkage all happens for you if you use the task survey plugin. You can look on the wiki for more...
  • Vineeth: I want a way in which if a survey is filled in by the user the response are stored in the survey response...
  • Mark Stanger: This functionality doesn’t connect to an FTP server. See this line in the post above…...
  • Mark Stanger: The report page is back-end XML so there’s no way to directly manipulate the behavior of that...