The CreatorCon Call for Content is officially open! Get started here.

How to establish snUtil feature in servicenow without installing plugin?

PrinceK78263780
Kilo Contributor

Requirement:
In the Incident form, there is a field called Watchlist (field type: glide_list). This field allows adding multiple users, but to remove a user, we currently need to unlock the field first.

I need to develop a solution where, without unlocking the field, an X (cross) hyperlink appears beside each user, and clicking that X should remove the respective user.

Is it possible to achieve this?

1 ACCEPTED SOLUTION

Vishal_Jaiswal
Mega Guru

Hi @PrinceK78263780 ,

 

Create a on-change client script on the field watchlist and refer the below script.

function onChange(control, oldValue, newValue, isLoading, isTemplate) {

    // --- Helper Functions ---
    // These are now nested correctly so they are always available.

    function snuRemoveFromList(event) {
        event.preventDefault();
        var spanElement = this.parentElement;
        var fieldName = spanElement.getAttribute("data-field");
        var valueToRemove = spanElement.getAttribute("data-value");

        var oldValues = g_form.getValue(fieldName).split(',');
        var newValues = oldValues.filter(function(item) {
            return item.trim() !== valueToRemove.trim();
        });

        g_form.setValue(fieldName, newValues.join(','));
        
        // Corrected: Only call the function with a delay.
        setTimeout(renderCustomGlideList, 100);
    }

    function renderCustomGlideList() {
        try {
            document.querySelectorAll('div[type=glide_list]').forEach(function(elm) {
                var field = elm.id.split('.')[2];
                if (field !== '<watchlist_Field_value_name>') {
                    return;
                }

                var table = g_form.getGlideUIElement(field).reference;
                if (!table || table === 'null') return;

                var displayP = elm.nextSibling.querySelector('p');
                if (!displayP) return;
                displayP.style.display = 'inline';

                var options = document.querySelectorAll(`select[id$=${field}] option`);
                var labels = [...options].map(option => option.innerText);
                var values = elm.nextSibling.querySelector('input[type=hidden]').value.split(',');

                if (labels.length != values.length) return;

                var links = [];
                for (var i = 0; i < labels.length; i++) {
                    if (values[i] != "") {
                        var removeButton = `<a title="Remove" class="remove icon icon-cross" href="#" style="font-size:8pt; color:red; padding-right:4px; vertical-align: middle;"></a>`;
                        var itemHtml = `<span style='white-space: nowrap' data-field="${field}" data-value="${values[i]}">` +
                            removeButton +
                            `<a href="/${table}.do?sys_id=${values[i]}" target="_blank">${labels[i]}</a>` +
                            `</span>`;
                        links.push(itemHtml);
                    }
                }

                var html = links.join(', ');
                displayP.innerHTML = html;

                displayP.querySelectorAll('.remove').forEach(function(removeElm) {
                    removeElm.addEventListener('click', snuRemoveFromList);
                });
            });
        } catch (e) {}
    }

    // --- Main Execution Logic ---
    
    // If the field is cleared, do nothing (but let the UI clear the links).
    if (newValue === '') {
       return;
    }
    
    // This single call will now handle both the onLoad event and every subsequent onChange event.
    renderCustomGlideList();
}

 

View solution in original post

2 REPLIES 2

Vishal_Jaiswal
Mega Guru

Hi @PrinceK78263780 ,

 

Create a on-change client script on the field watchlist and refer the below script.

function onChange(control, oldValue, newValue, isLoading, isTemplate) {

    // --- Helper Functions ---
    // These are now nested correctly so they are always available.

    function snuRemoveFromList(event) {
        event.preventDefault();
        var spanElement = this.parentElement;
        var fieldName = spanElement.getAttribute("data-field");
        var valueToRemove = spanElement.getAttribute("data-value");

        var oldValues = g_form.getValue(fieldName).split(',');
        var newValues = oldValues.filter(function(item) {
            return item.trim() !== valueToRemove.trim();
        });

        g_form.setValue(fieldName, newValues.join(','));
        
        // Corrected: Only call the function with a delay.
        setTimeout(renderCustomGlideList, 100);
    }

    function renderCustomGlideList() {
        try {
            document.querySelectorAll('div[type=glide_list]').forEach(function(elm) {
                var field = elm.id.split('.')[2];
                if (field !== '<watchlist_Field_value_name>') {
                    return;
                }

                var table = g_form.getGlideUIElement(field).reference;
                if (!table || table === 'null') return;

                var displayP = elm.nextSibling.querySelector('p');
                if (!displayP) return;
                displayP.style.display = 'inline';

                var options = document.querySelectorAll(`select[id$=${field}] option`);
                var labels = [...options].map(option => option.innerText);
                var values = elm.nextSibling.querySelector('input[type=hidden]').value.split(',');

                if (labels.length != values.length) return;

                var links = [];
                for (var i = 0; i < labels.length; i++) {
                    if (values[i] != "") {
                        var removeButton = `<a title="Remove" class="remove icon icon-cross" href="#" style="font-size:8pt; color:red; padding-right:4px; vertical-align: middle;"></a>`;
                        var itemHtml = `<span style='white-space: nowrap' data-field="${field}" data-value="${values[i]}">` +
                            removeButton +
                            `<a href="/${table}.do?sys_id=${values[i]}" target="_blank">${labels[i]}</a>` +
                            `</span>`;
                        links.push(itemHtml);
                    }
                }

                var html = links.join(', ');
                displayP.innerHTML = html;

                displayP.querySelectorAll('.remove').forEach(function(removeElm) {
                    removeElm.addEventListener('click', snuRemoveFromList);
                });
            });
        } catch (e) {}
    }

    // --- Main Execution Logic ---
    
    // If the field is cleared, do nothing (but let the UI clear the links).
    if (newValue === '') {
       return;
    }
    
    // This single call will now handle both the onLoad event and every subsequent onChange event.
    renderCustomGlideList();
}