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

Hi @Markus Kraus ,

Thank you so much for the detailed instructions. That's amazing! I could update the field just by clicking on the button. Thank you!

However, there is just one thing. After clicking on the button, the field value change is not reflected on the list unless I refresh the browser.

Could you give me some advice to make the new value reflected on the list just after clicking on the button, please?

Have you added this line to the replacements?

.replace('::item[field]', 'item[field]') // disable two-way binding on widget-data-table

The data table widget adds a record watcher, this will update the table when new, deleted and records are generally updated. However the updates don't reflect in the columns because the data is one way bound (meaning it will only be added initially to the table, but not updated when the underlying data changes) - the replacement above will fix this.

However: This recordwatcher only initializes when there is an actual filter applied (e.g. incident=INCxyz), without a filter applied to the table, the watcher will simply not be initialized and therefore no changes will be applied to the data table.

I'll quickly check if i can find a good workaround for the "no recordwatcher without predefined filter/query" problem.

Hi @Markus Kraus ,

Yes, I replaced that section, but the new value is not reflected unless refreshing the page (browser).

Could you please check the entire Client Controller script of the custom widget?

 

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

	c.click = function (item) {
	var ga = new window.GlideAjax('global.ListEditUtils').setScope('global');
	ga.addParam('sysparm_name', 'toggleItem');
	ga.addParam('item', item.sys_id);
	ga.getXMLAnswer(function (result) {
		// at this point the data table widget should update itself automatically
	});

        /*
        item has the following structure (number, priority, short_description depend on what you selected as fields in the instance options):
        {
        	sys_id: '<Sys ID of the item>',
        	targetTable: '<table name of the item>',
        	number: {
        		display_value: "INC00000XYZ"
        		label: "Number"
        		limit: "40"
        		type: "string"
        		value: "INC00000XYZ"
        	},
        	priority: {
        		display_value: "5 - Planning",
        		label: "Priority",
        		limit: "40",
        		type: "integer",
        		value: "5"
        	},
        	short_description: {
        		display_value: "My Short Description",
        		label: "Short description",
        		type: "string",
        		value: "My Short Description"
        	}
        }
        */

        /*if (item.number.value == 'INC0000001') {
        	alert('INC0000001');
        }*/
		};

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

        var template = tableScope.widget.template
            .replace('::item[field]', '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);
    };
};

 

 

Hi @Aki18 i just realised, that the replacement for the two way binding was incorrect... it should be a regex. Additionally angular needs a little "push" to actually do apply the update. The following code will work 🙂

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) {
			scope.$evalAsync(function () {}); // tell angularjs to apply the visual changes
		});
	};

	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);
	};
};

Hi @Markus Kraus ,

Thank you so much for the troubleshooting. We are almost there, but still got some issues on it...

 

1. It takes about 3 - 4 seconds for the change to be reflected on the list.

--> Can we shorten the time for the new value to be reflected on the list?

 

2. The second row is not reflected unless I refresh the page(browser).

--> After clicking on button of the first row, the field value gets changed without refreshing the page, but after that, when clicking on the second button of the following row, somehow the field value is not changed unless I refresh the page...

 

It's a kind of strange behavior, but do you have any ideas why this happens?

 

Aki18_0-1686408629595.png