Guide: ServiceNow Portal Scrabble Widget (Passing data from Client to Server and back)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎02-17-2025 12:44 AM
Contents
Introduction
Over Christmas, my partner and I were playing our annual game of Scrabble, during which, as always, I was thoroughly trounced. While recovering from my loss and contemplating the start of this blog, I thought it would be fantastic if there were a ServiceNow Widget that could check if my words were legal and calculate their points. Thus, this Widget was born.
This guide will walk you through building a ServiceNow Widget that interacts with an external API to validate Scrabble words and calculate their score. You'll learn how to pass data between the client and server, make API calls, and handle responses
First things first, I went in search of a Scrabble API. I found Wordnik and requested an API Key. If you wish to follow this guide, I suggest doing the same. Alternatively, if you find a better API, feel free to use that.
For this Widget we're going to complete the following tasks
- Create a Script Include to store the Server Side code
- Create a Widget to pass data from the client to the server
- Create a Service Portal page to display our widget
- Create a System Property to store our API Key.
Creating our Widget
In your ServiceNow Instance, navigate to Service Portal > Widget and create new.
I've named mine "ServiceNow Scrabble checker" but you can call yours whatever you want.
For the Body HTML Template we're going to create a button called "inputWord" and add an "ng-click" directive to call a function in our Client Script called checkWord
We've also got an ng-if to show the reply from the server, if there is one.
<div>
<p>Enter a word below and click the button underneath to check if your word is valid in Scrabble!</p>
<input type="text" ng-model="inputWord"><br><br>
<button class="btn btn-primary" ng-click="c.checkWord(inputWord)">Check word</button><br><br>
<p ng-if="c.message">{{c.message}}</p>
</div>
Now for our Client Script
api.controller = function($scope) {
/* Widget controller */
var c = this;
/**
* Function to check if a given word is valid in Scrabble and get its score.
* This function is called from the HTML listed above, with the Word passed as a parameter.
*/
c.checkWord = function(word) {
// Set the action and word in the data object for reference
c.data.action = 'checkScrabbleWord';
c.data.word = word;
// Create an object to send to the server for validation
var dataObj = {
action: 'checkScrabbleWord',
word: word
};
// Call the server script to check the word
c.server.get(dataObj).then(function(response) {
// Extract response data (score and validity)
c.data.score = response.data.score;
c.data.isValid = response.data.isValid;
// Default message while checking the word
c.message = "Checking word...";
// Update the message based on validity of the word
if (c.data.isValid) {
c.message = `Congratulations! Your word "${word}" is a Scrabble word, and you've earned ${c.data.score} points.`;
} else {
c.message = `"${word}" is not a valid Scrabble word. Better luck next time!`;
}
// Clear the input word after processing
c.data.word = '';
});
}
};
And finally our Server side code
(function() {
// Check if there is input data and it contains required properties (action and word)
if (input && input.action && input.word) {
// Proceed only if the action is 'checkScrabbleWord'
if (input.action === 'checkScrabbleWord') {
// Call the checkWord function to validate the Scrabble word
var returnedData = checkWord(input.word);
// A little logging for troubleshooting purposes..
gs.info("Input returned data " + JSON.stringify(returnedData));
// If the word is invalid, set score to 0 and mark as invalid
if (returnedData.valid === false) {
data.score = 0;
data.isValid = false;
} else {
// Otherwise, set the score and mark the word as valid
data.score = returnedData.value;
data.isValid = true;
}
}
}
/**
* Function to check if a word is valid in Scrabble by calling an external API.
* @Param {string} word - The word to check.
* @returns {Object} - An object containing the validity and score of the word.
*/
function checkWord(word) {
try {
// Convert the word to lowercase, as that's what the API Expects..
var newWord = word.toLowerCase();
// Create a REST API request to validate the Scrabble word
var r = new sn_ws.RESTMessageV2('CheckScrabbleWord', 'Default GET');
// Set the API key
r.setStringParameterNoEscape('api_key', 'YOUR_API_KEY');
// Pass the word parameter to the API request
r.setStringParameterNoEscape('word', newWord);
// Execute the REST call and get the response
var response = r.execute();
var responseBody = response.getBody();
// Initialize an empty object to store response data
var returnedData = {};
// A little more logging for troubleshooting
gs.info("Resp: " + JSON.stringify(responseBody));
// Parse the JSON response
var responseObject = JSON.parse(responseBody);
// Check if the API returned a 404 (word not found)
if (responseObject.statusCode == 404) {
returnedData.value = 0;
returnedData.valid = false;
} else {
// If valid, store the Scrabble score and set validity to true
returnedData.value = responseObject.value;
returnedData.valid = true;
}
gs.info("Returned data " + JSON.stringify(returnedData));
return returnedData;
} catch (ex) {
// Log any errors that occur during the API request
var message = ex.message;
gs.info(message);
}
}
})();
You promised us a Script include and System property?
Guilty as charged - you're correct, I did say we would put the Server Script in to a Script include, and the API key in a system property. My personal development preference is to always do all the work in a Widget and then move everything to where they should be. So now our widget is working, lets do that.
So, for the system property, open up your sys_properties table and create a new property called "scrabbleAPIKey"
For the Script Include, navigate to System definition > Script Includes and create a new one. Lets call this ScrabbleWidgetUtils
Create a new function called checkScrabbleWord and move the code from our Widget Server script function checkWord() in to it as below.
Change the line following line
r.setStringParameterNoEscape('api_key', 'YOUR_API_KEY');
To
r.setStringParameterNoEscape('api_key', gs.getProperty('scrabbleAPIKey'));
So we end up with the following script include
var ScrabbleWidgetUtils = Class.create();
ScrabbleWidgetUtils.prototype = {
initialize: function() {},
checkScrabbleWord: function(word) {
try {
var newWord = word.toLowerCase();
var r = new sn_ws.RESTMessageV2('CheckScrabbleWord', 'Default GET');
// Updated to use our System Property.
r.setStringParameterNoEscape('api_key', gs.getProperty('scrabbleAPIKey'));
r.setStringParameterNoEscape('word', newWord);
var response = r.execute();
var responseBody = response.getBody();
var returnedData = {};
gs.info("Resp: " + JSON.stringify(responseBody))
var responseObject = JSON.parse(responseBody);
if (responseObject.statusCode == 404) {
returnedData.value = 0;
returnedData.valid = false;
} else {
returnedData.value = responseObject.value;
returnedData.valid = true;
}
gs.info("Returned data " + JSON.stringify(returnedData))
return returnedData;
} catch (ex) {
var message = ex.message;
gs.info(message);
}
},
type: 'ScrabbleWidgetUtils'
};
Your Widget Server script should also be updated to look like this.
(function() {
if (input && input.action && input.word) {
if (input.action === 'checkScrabbleWord') {
var returnedData = checkWord(input.word);
gs.info("Input returned data " + JSON.stringify(returnedData))
if (returnedData.valid === false) {
data.score = 0;
data.isValid = false;
} else {
data.score = returnedData.value;
data.isValid = true;
}
}
}
function checkWord(word) {
var result = new ScrabbleWidgetUtils().checkScrabbleWord(word);
return result;
}
})();
Create a new Portal Page by Navigating to Service Portal > Pages and clicking "New"
Fill out the details as above and click "Submit" in the "Related Links" in the bottom left, select "Open in Designer"
Create a new section and drag our widget across
We can then navigate to our page https://YOUR_INSTANCE.service-now.com/esc?id=servicenow_scrabble_checker and give it a whirl!
Conclusion
And there we are. We've created a widget that takes data from the Portal, sends it to the Server, which in then calls a 3rd party API. We've then dealt with the response and displayed that data back in to the client.