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...