ryannnnnnnnnnnn
Tera Contributor

There have been a number of questions raised asking how we can control more than just the text of Field Messages.  Here's how...

Intended results of this example:

When a customer types in a text box, I want to use the showFieldMsg function to dynamically list any glossary terms in a custom table, and when applicable, add hyperlinks to those sections of our intranet.

find_real_file.png

 

Architecture:

1 client script

1 script include (not necessary, but this complies with Service-Now's best practices, specifically that GlideRecord calls should only occur on the server)

 

Client Script:

function onChange(control, oldValue, newValue, isLoading, isTemplate) {
    if (isLoading || newValue === '') {
        return;
    }

    var fieldOfInterest = 'u_full_description_test';
    var words = getWordsFromField(fieldOfInterest);                 // build an array of all words in our field
    var abbreviationsUsed = [];                                     // this will hold a list of all abbreviations info found in our field of interest

    for (var i = 0; i < words.length; i++) {                        // loop through every word to test it as an abbreviations
        try {
            var ga = new GlideAjax('MyAJAXScript');                 // make a call to 'MyAJAXScript' script
            ga.addParam('sysparm_name', 'getAbbreviationInfo');     // invoke the 'getAbbreviationInfo' function in the 'MyAJAXScript' script
            ga.addParam('sysparm_abbreviation', words[i]);  	    // pass in the potential abbreviation within 'sysparm_abbreviation'
            ga.getXMLWait();

            var answer = ga.getAnswer();
            answer = answer.evalJSON();                             // we're expecting a multipart result with {abbreviation, fullterm, link} in JSON format

            if (!answer || !answer.abbreviation)                    // this word is not an abbreviation
                continue;

            if (isAbbreviationUsed(answer.abbreviation, abbreviationsUsed))   // if we've already come across the abbreviation
                continue;

            g_form.showFieldMsg(fieldOfInterest, 'loading...');     // put a placeholder in the Field Message for now (we'll overwrite this once all Field Messages are set up)
            var abbrevObject = {
                abbreviation: answer.abbreviation,
                fullTerm: answer.fullTerm,
                link: answer.link
            };
            abbreviationsUsed.push(abbrevObject);
        } catch (err) {
            console.log(err);
        }
    }

    if (abbreviationsUsed.length == 0)                              // no abbreviations found, so we can stop here
        return;

    var elements = getFieldMessageElements(fieldOfInterest);        // get all Field Message elements for the given field
    for (var j = 0; j < elements.length; j++)
        elements[j].innerHTML = getText(abbreviationsUsed[j]);      // update the given Field Message with one of our abbreviations found
}

// build an array of the words found in a given field

function getWordsFromField(field) {
    var text = g_form.getValue(field);                              // get the original text from the given field

    text = text.replace(/<(?:.|\n)*?>/gm, '');                      // remove HTML tags
    text = text.replace(/nbsp;/g, '');                              // remove any "&nbsp;" strings (only remove the "nbsp;" if we include the "&", it'll be interpreted as a space)
    text = text.replace(/[.,\/#!$%\^&\*;:{}=\-_`~()'"]/g, ' ');     // replace punctuation with spaces (adding a space helps with text using embedded links)

    var words = text.split(' ');                                    // build array of all words
    words = words.filter(Boolean);                                  // remove all empty entries (ie: ['i', 'had', '', '', 'empty', 'entries'] becomes ['i', 'had', 'empty', 'entries'])
    return words;
}

// check if the given abbreviation is already in our array of the abbreviations we've encountered

function isAbbreviationUsed(abbreviation, abbreviationsUsed) {
    var abbrev = abbreviation.toLowerCase();
    for (var i = 0; i < abbreviationsUsed.length; i++)
        if (abbrev == abbreviationsUsed[i].abbreviation.toLowerCase())
            return true;
    return false;
}

// get all Field Message DOM elements for a given field

function getFieldMessageElements(field) {
    try {
        var inputElement = g_form.getControl(field);                // ask Service-Now for a DOM reference to our field of interest
        var parentElement = inputElement.parentElement;             // get the field's parent because the Field Messages are siblings to inputElement    
        var childrenElements = parentElement.childNodes;            // get all of the children (inputElement's siblings and itself)
        for (i = 0; i < childrenElements.length; i++) {
            if (childrenElements[i].id.indexOf('_fieldmsg') < 0)    // we want to find the child with '_fieldmsg' in the ID
                continue;

            return childrenElements[i].childNodes;                  // we've found the container of the Field Message(s)
        }
    }
    catch (err) {
        console.log(err.message);
    }

    return null;
}

// build text and hyperlink (when one exists) for a given abbreviation object

function getText(abbreviationObject) {
    var text = abbreviationObject.abbreviation + ' - ' + abbreviationObject.fullTerm;
    if (abbreviationObject.link.length)
        text = '<a href="' + abbreviationObject.link + '">' + text + '</a>';
    return text;
}

 

Script Include:

var MyAJAXScript = Class.create();
MyAJAXScript.prototype = Object.extendsObject(AbstractAjaxProcessor, {

    // gets abbreviation information for a given word

    getAbbreviationInfo: function () {
        var obj = {};
        var abbreviation = this.getParameter('sysparm_abbreviation');

        var gr = new GlideRecord('u_latham_abbreviations');
        gr.addQuery('u_abbreviation', abbreviation);
        gr.query();

        if (gr.next()) {                                            // if the word is a known abbreviation
            obj.abbreviation = gr.u_abbreviation + '';              // add an empty string to implicitly cast as a string (otherwise it'll be an object)
            obj.fullTerm = gr.u_full_term + '';
            obj.link = gr.u_link + '';
        }

        var json = new JSON();
        var encodedObj = json.encode(obj);
        return encodedObj;
    },

    type: 'MyAJAXScript'
});

 

This all assumes a custom table with three fields:

  • u_abbreviation
  • u_full_term
  • u_link

To give a detailed explanation of the most important function...

// get all Field Message DOM elements for a given field

function getFieldMessageElements(field) {
    try {
        var inputElement = g_form.getControl(field);                // ask Service-Now for a DOM reference to our field of interest
        var parentElement = inputElement.parentElement;             // get the field's parent because the Field Messages are siblings to inputElement    
        var childrenElements = parentElement.childNodes;            // get all of the children (inputElement's siblings and itself)
        for (i = 0; i < childrenElements.length; i++) {
            if (childrenElements[i].id.indexOf('_fieldmsg') < 0)    // we want to find the child with '_fieldmsg' in the ID
                continue;

            return childrenElements[i].childNodes;                  // we've found the container of the Field Message(s)
        }
    }
    catch (err) {
        console.log(err.message);
    }

    return null;
}

... what we're doing here is asking for the Field Message elements so we can change the content within each.  We start by asking Service-Now to give us DOM access to the appropriate field.  Unfortunately, it gives us access to a sibling element, so to get to the right DOM element, we request access to the parent and then to the parent's children (ie: all siblings).  The child we want to work with has "_fieldmsg" in its ID value.  Every Field Message will be a child of this particular sibling, so finally, we return the child nodes of the element containing "_fieldMsg".

Can this break? Not in the current version. 

Is it future-proof? ...Is anything? No, but we're using Service-Now's built-in function to request DOM access, so it's as safe as Service-Now will allow!

I hope this helps,

Ryan

 

 

 

 

 

Version history
Last update:
‎09-14-2018 04:50 PM
Updated by: