Join the #BuildWithBuildAgent Challenge! Get recognized, earn exclusive swag, and inspire the ServiceNow Community with what you can build using Build Agent.  Join the Challenge.

How to customize Data Table widget to make it show True/False as checkbox and editable by clicking?

Aki18
Tera Contributor

On Service Portal, we would like to make the Data Table widget show the "True/False" field as checkbox and editable by just clicking on it, but not sure about how to customize the widget. Can it be achieved by editing HTML/CSS or server script?

Could someone please advise me on how to customize the cloned widget to meet the requirements?

 

Aki18_0-1686126592169.png

 

Best Regards,

Aki

1 ACCEPTED SOLUTION

Hm what could also be, is that the GlideAjax's callback is actually executed before the record watcher callback. This would explain the strange delay...

I would assume you can improve the behavior by not relying on the recordwatcher anymore and just apply the changed value by your own:

1.) In the Client Callable Script Include, return the new value for your field, e.g.:

var ListEditUtils = Class.create();
ListEditUtils.prototype = Object.extendsObject(AbstractAjaxProcessor, {
	
	toggleItem: function () {
		var inc = new GlideRecord('incident');
		if (inc.get(this.getParameter('item'))) {
			inc.active = !inc.active;
			inc.setWorkflow(false);
			inc.update();
			return inc.active.toString(); // return the new value
		}
		
		return 'false';
	},

    type: 'ListEditUtils'
});

2.) In the client controller, modify the c.click like this (to apply the updated field value on your own, of course you wont be using the active field - so please modify the script as you would also for the script include above):

// ...
	c.click = function (item) {		
		var ga = new window.GlideAjax('global.ListEditUtils');
		ga.addParam('sysparm_name', 'toggleItem');
		ga.addParam('item', item.sys_id);
		ga.getXMLAnswer(function (result) {
			item.active.display_value = result;
			tableScope.$apply(); // rerender the table
		});
	};
//...

View solution in original post

13 REPLIES 13

Hm strange, i just saw that i had a typo in the GlideAjax callback, it should not be scope, but tableScope.

The full widget client controller:

api.controller=function($compile) {
	var c = this;

	var tableScope = null;
	c.click = function (item) {		
		var ga = new window.GlideAjax('global.ListEditUtils');
		ga.addParam('sysparm_name', 'toggleItem');
		ga.addParam('item', item.sys_id);
		ga.getXMLAnswer(function (result) {
			tableScope.$evalAsync(function () {});
		});
	};

	c.init = function (tableElement) {
		tableScope = tableElement.scope();
		var filter = tableScope.data.filter;
		
		tableScope.click = c.click.bind(c);

		var template = tableScope.widget.template
			.replace(/::item\[field\]/g, 'item[field]') // disable two-way binding on widget-data-table
			.replace('</th>', '</th><th>Actions</th>')
			.replace('</td>', '</td><td ng-class="{selected: item.selected}"><button ng-click="click(item)">X</button></td>');
		tableElement.html(template);
		$compile(tableElement.contents())(tableScope);
	};
};

For me, this is working perfectly fine...

MarkusKraus_1-1686499730902.gif

 

Hi @Markus Kraus ,

Thank you so much for your support. After replacing the client controller script, the second row is now updated!

However, it still takes about 5 or more seconds for the value change to be reflected on the list...

I'm using the cloned Data table from URL definition widget to fix the table to "sc_task", but do you think it's related to the symptom? Or, is it just due to our demo instance's performance?

Here is the server script of the cloned widget:

(function() {
    deleteOptions(['table', 'field_list', 'filter', 'order_by', 'order_direction', 'order', 'maximum_entries']);
    /* if (input) {
    	data.table = input.table;
    	data.view = input.view;
    } else {
    	data.table = $sp.getParameter('table') || $sp.getParameter('t');
    	data.view = $sp.getParameter('view');
    }*/
    data.table = 'sc_task';

    if (!data.table) {
        data.invalid_table = true;
        data.table_label = "";
        return;
    }

    var gr = new GlideRecordSecure(data.table);
    if (!gr.isValid()) {
        data.invalid_table = true;
        data.table_label = data.table;
        return;
    }

    // page is where the record URLs go, URL parameter wins
    data.page_id = $sp.getParameter("target_page_id");
    if (!data.page_id) {
        var sp_page = $sp.getValue('sp_page');
        if (sp_page) {
            var pageGR = new GlideRecord('sp_page');
            pageGR.get(sp_page);
            data.page_id = pageGR.id.getDisplayValue();
        }
    }

    // widget parameters
    data.table_label = gr.getLabel();
    data.fields = $sp.getListColumns(data.table, data.view);
    copyParameters(data, ['p', 'o', 'd', 'filter', 'fixed_query']);
    copyParameters(data, ['relationship_id', 'apply_to', 'apply_to_sys_id']);
    data.filterACLs = true;
    data.show_new = true;
    data.show_keywords = true;
    data.show_breadcrumbs = true;
    data.fromUrl = true;
    data.useInstanceTitle = (options.use_instance_title == "true");
    data.headerTitle = data.useInstanceTitle ? options.title : gr.getPlural();
    data.enable_filter = input.enable_filter || options.enable_filter == true || options.enable_filter == "true";
    data.dataTableWidget = $sp.getWidget('widget-data-table', data);


    function copyParameters(to, names) {
        names.forEach(function(name) {
            data[name] = $sp.getParameter(name);
        })
    }

    // in case this widget is tied to the wrong instance type
    function deleteOptions(names) {
        names.forEach(function(name) {
            delete options[name];
        })
    }
})()

 

Hm what could also be, is that the GlideAjax's callback is actually executed before the record watcher callback. This would explain the strange delay...

I would assume you can improve the behavior by not relying on the recordwatcher anymore and just apply the changed value by your own:

1.) In the Client Callable Script Include, return the new value for your field, e.g.:

var ListEditUtils = Class.create();
ListEditUtils.prototype = Object.extendsObject(AbstractAjaxProcessor, {
	
	toggleItem: function () {
		var inc = new GlideRecord('incident');
		if (inc.get(this.getParameter('item'))) {
			inc.active = !inc.active;
			inc.setWorkflow(false);
			inc.update();
			return inc.active.toString(); // return the new value
		}
		
		return 'false';
	},

    type: 'ListEditUtils'
});

2.) In the client controller, modify the c.click like this (to apply the updated field value on your own, of course you wont be using the active field - so please modify the script as you would also for the script include above):

// ...
	c.click = function (item) {		
		var ga = new window.GlideAjax('global.ListEditUtils');
		ga.addParam('sysparm_name', 'toggleItem');
		ga.addParam('item', item.sys_id);
		ga.getXMLAnswer(function (result) {
			item.active.display_value = result;
			tableScope.$apply(); // rerender the table
		});
	};
//...

Hi @Markus Kraus ,

Thank you so much for the modification! Now the field is updated on the list just after clicking the button, amazing!

I really appreciate it and accepting it as Solution. Thank you so much!

*BTW: I have another question in my other post below regarding how to keep the page scroll position even after refreshing pages. So if you already have some good idea, I would also appreciate your advice.

https://www.servicenow.com/community/developer-forum/how-to-keep-the-page-scroll-position-even-after...