Guide: ServiceNow Portal Scrabble Widget (Passing data from Client to Server and back)

SNReece
Tera Contributor

Contents

  1. Introduction
  2. Creating our Widget
  3. Creating a Script Include and System Property
  4. Conclusion

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"

ReeceLovell_6-1739781586835.png

 

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"

ReeceLovell_7-1739781711244.png

 

Fill out the details as above and click "Submit" in the "Related Links" in the bottom left, select "Open in Designer"

 

ReeceLovell_8-1739781734015.png

 

Create a new section and drag our widget across

ReeceLovell_9-1739781759135.gif

 

We can then navigate to our page https://YOUR_INSTANCE.service-now.com/esc?id=servicenow_scrabble_checker and give it a whirl!

ReeceLovell_10-1739781780536.gif

 

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.

 

0 REPLIES 0