Skip to main content

Service Portal

order guides

use cascade variables with variable names matching the catalog item variables rather than variable mappings from rule base entries, which doesn't seem to work too well in the portal

Widget Coding

submit value to server script - method 1:
take the value of a html field, and submit so that the server script fires
Client script: = angular.element($('#dupl_field')).val();

submit value to server script - method 2:
take the value of multiple html fields, and pass these to the server script 
Client script:
            action: "get_dupls",
            msg : "hello server",
            sn_table : angular.element($('#tbl_name')).val(),
            duplfield : angular.element($('#dupl_field')).val()
server script:
if (input && input.action === "get_dupls"){
        data.duplresult='blah blah';

Click a button:

<br/><br/><button ng-click="runtheoperation()" class="btn btn-danger action-btn pull-left">Click to check dupl</button>

Client script:

function($scope) {
  /* widget controller */
  var c = this;
    $scope.runtheoperation = function() {
      alert('the button was clicked!');

Shifting array returned from server script into a client script and back into textarea
this nicely formats the array by shifting each element to a new line in the textarea;


(see widget example below for full HTML and client/server scripts)

Example Widget to run a glideAggregate to check duplicates on a table:

A simple widget to return duplicate records
<title>Check ServiceNow Table for duplicates</title>
<div class='container'>
<!-- your widget template -->
  <label>Enter table name here</label>
    <input value ='sys_user' id='tbl_name'>
  <label>Enter field name here</label>
    <input value='email' id='dupl_field'>
  <label>Custom query string</label>
    <input value='active=true' id='custom_query'>
  <br/><br/><button ng-click="runtheoperation()" class="btn btn-danger action-btn pull-left">Click to check dupl</button>
   <textarea id='results_q' rows="10" cols="100">{{c.duplrecordsList}}</textarea>
<h1>Total list:{{c.duplrecordsSize}}</h1>
<!--<div class='div_results'>

.container {
  clear: both;
  align-content: left;

.container input {
  width: 100px;
  clear: both;

.container label {
  width: 150px;
  clear: both;

.container textarea {

function($scope) {
  /* widget controller */
  var c = this;
    $scope.runtheoperation = function() {
        //console.log("message", 'run the op');
               action: "get_dupls",
             msg : "Running glideaggregate to check for duplicates...",
               sn_table : angular.element($('#tbl_name')).val(),
               duplfield : angular.element($('#dupl_field')).val(),
               customQuery : angular.element($('#custom_query')).val()
              // alert('SIZE: ' +;
              //alert('test2: ' +;



(function() {
  /* populate the 'data' object */
  /* e.g., data.table = $sp.getValue('table'); */
    if (input && input.action === "get_dupls"){
        data.duplrecordsList=(checkDuplUsers(input.customQuery, input.duplfield,input.sn_table));
function checkDuplUsers(customQuery, fieldParam, table){
  var ga= new GlideAggregate(table);
  ga.addAggregate('COUNT', fieldParam);
  if (customQuery){
  ga.addHaving('COUNT', '>', 1);
    var dupls=[];
    dupls.push("The following " + ga.getRowCount() + " users are duplicate on " + fieldParam);
  while ( {
        var DuplLine=ga.getAggregate('COUNT', fieldParam) + " => " + ga.getElement(fieldParam);
    return dupls;

Create a Service Portal 

Create a basic service portal

navigate to Service Portal > Service Portal configuration

navigate to Service Portal > Portals, then click New.

complete the page as per

to begin with, you can configure all of the options as per the demo sp settings

Branding a portal

via /sp_config?id=branding_editor

have a play with the various options: navbar background, page background, etc

for more in-depth branding options, consult

the theme of the portal can also be altered by adding a new theme (sp_theme table) and editing the CSS variables as desired

Create a page

an easy way to do this: go to Service Portal Configuration> page editor> select the 'index' page> click on the page box in the map> scroll down and click 'clone page'

locate the cloned page and then rename it and modify it to suit, and reference it in the portal (service portals> portal> the portal you just created)

branding a page

navigate to the page in designer and change for example the background color (replace $sp-homepage-bg with orange) {
background-color: orange;
}, main.body {
    padding-top: 0px !important;


Popular posts from this blog

ServiceNow check for null or nil or empty (or not)

Haven't tested these all recently within global/local scopes, so feel free to have a play! option 1 use an encoded query embedded in the GlideRecord , e.g.  var grProf = new GlideRecord ( 'x_cls_clear_skye_i_profile' ); grProf . addQuery ( 'status=1^ owner=NULL ' ); grProf . query (); even better use the glideRecord  addNotNullQuery or addNullQuery option 2 JSUtil.nil / notNil (this might be the most powerful. See this link ) example: if ( current . operation () == 'insert' && JSUtil . notNil ( current . parent ) && ! current . work_effort . nil ())  option 3 there might be times when you need to get inside the GlideRecord and perform the check there, for example if the code goes down 2 optional routes depending on null / not null can use gs.nil : var grAppr = new GlideRecord ( 'sysapproval_approver' ); var grUser = new GlideRecord ( 'sys_user' ); if ( grUser . get ( 'sys_id' , current . approver )){

Get URL Parameter - server side script (portal or classic UI)

Classic UI : var sURL_editparam = gs . action . getGlideURI (). getMap (). get ( ' sysparm_aparameter ' ); if ( sURL_editparam == 'true' ) { gs . addInfoMessage ( 'parameter passed ); } Portal : var sURL_editparam = $sp . getParameter ( " sysparm_aparameter " ); if ( sURL_editparam == 'true' ) { gs . addInfoMessage ( 'parameter passed ); }