Editing the cells

We can make the Table editable with a callback that fires on change.

  • ts.ui.get('#table1', table => {
    	table.rows([
    		['Single asterisks is used for *italic text*'],
    		['Double asterisks is used for **strong text**'],
    		['Single backtick is used for `monotype text`'],
    		['Double tilde can be used to ~~Strike text~~']
    	]).editable(function onedit(rowindex, cellindex, value) {
    		console.log('Send to backend: ' + value);
    		table.cell(rowindex, cellindex, value);
    	});
    });

All cells are assumed editable unless negated in the column definition.

  • ts.ui.get('#table6', table => {
    	table.cols([
    		{label: 'Edit'},
    		{label: 'Edit'},
    		{label: 'Don\'t edit!', editable: false}
    	]).rows([
    		['V', 'V', 'X']
    	]).editable(function onedit(ri, ci, value) {
    		this.cell(ri, ci, value);
    	});
    });

If you switch to verbose syntax, you can also disable editing per row.

  • ts.ui.get('#table7', table => {
    	table.rows([
    		{ cells: ['X', 'X', 'X'], editable: false},
    		{ cells: ['V', 'V', 'V']},
    		{ cells: ['V', 'V', 'V']}
    	]).editable(function onedit(ri, ci, value) {
    		this.cell(ri, ci, value);
    	});
    });

You can override both columns and rows by specifying editable:true on the individual cells. Again, this would best be done using verbose syntax.

Multiline editing

You can enter newlines by holding SHIFT on ENTER. You will need to enter two newlines to create a paragraph. Note however that we only show the first paragraph unless the column is set to wrap.

  • var popup = ts.ui.Notification;
    ts.ui.get('#table8', table => {
    	table.cols([
    		{label: 'Wrapped column', wrap: true},
    		{label: 'Unwrapped column'}
    	]).rows([
    		[
    			'Hold `SHIFT` while pressing `ENTER` to add a second paragraph.',
    			'New paragraph ignored!'
    		]
    	]).editable(function onedit(ri, ci, value) {
    		table.cell(ri, ci, value);
    	});
    });

Input validation

Note that the actual update is a manual proces. This will give you a break to validate the input. In this example, we'll show the errors in a Notification.

  • var popup = ts.ui.Notification;
    ts.ui.get('#table2', table => {
    	table.rows([
    		[1, 1, 2, 3, 5],
    		[8, 13, 21, 34, 55]
    	]).editable(function onedit(ri, ci, value) {
    		value = Number(value);
    		if(isNaN(value)) {
    			table.invalid(ri, ci);
    			popup.error('Please type a number.', {
    				onaccept: function() {
    					table.focus(ri, ci);
    				}
    			});
    		} else {
    			console.log('Send to backend: ' + value);
    			table.cell(ri, ci, value);
    		}
    	});
    });

We can report the errors in a less annoying way if we pass a string as we mark the cells invalid. The message will show in the statubar when the cell is focused. We'll also initialize the statusbar with an empty string so that it doesn't awkwardly pop into existance.

  • function format(value) {
    	return new Date(value).toDateString();
    }
    function valid(value) {
    	return !isNaN(new Date(value).getTime());
    }
    ts.ui.get('#table3', table => {
    	table.status('').rows([
    		[{ value: '1985-10-26', text: format('1985-10-26') }],
    		[{ value: '2015-10-21', text: format('2015-10-26') }],
    		[{ value: '1955-11-12', text: format('1955-11-12') }]
    	]).editable(function onedit(ri, ci, value) {
    		if(valid(value)) {
    			console.log('Send to backend: ' + value);
    			table.cell(ri, ci, {
    				value: value,
    				text: format(value),
    			});
    		} else {
    			var message = 'Please use the format *YYYY-MM-DD*';
    			table.invalid(ri, ci, message);
    		}
    	});
    });

When the cell is eventually updated, it automatically becomes valid. Let's talk more about the difference between text and value in the example above.

Text versus value

It doesn't always matter, but it's important to note that we edit the value while we show the text. Here's another example to illustrate the difference.

  • ts.ui.get('#table4', table => {
    	table.rows([
    		[
    			{ value: 1000, text: '$1,000.00'},
    			{ value: 2000, text: '$2,000.00'},
    			{ value: 3000, text: '$3,000.00'}
    		]
    	]).editable();
    });

This way, we can edit, validate and persist the data in a uniform format (in this case, JavaScript numbers) while displaying a regionalized or otherwise customized format. We can exploit this creatively, by editing a document ID and showing the document title, for example. Remember to update verbosely:

table.cell(rowindex, cellindex, {
	text: '4,000.00',
	value: 4000
});

— because otherwise the text becomes identical to the value. Also note that the editable callback will always serve the value as a string, so it may be nescessary to convert it to a number. Here's the more complete example.

  • function format(n) {
    	return '$' + n.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",")
    }
    ts.ui.get('#table5', table => {
    	table.rows([
    		[
    			{ value: 1000, text: format(1000)},
    			{ value: 2000, text: format(2000)},
    			{ value: 3000, text: format(1000)}
    		]
    	]).editable(function(ri, ci, value) {
    		value = Number(value);
    		if(isNaN(value)) {
    			table.invalid(ri, ci);
    		} else {
    			table.cell(ri, ci, {value: value, text: format(value)});
    		}
    	});
    });

Here's an overview of the editing related features.

API for knowing when something is invalid / everything is valid.

How to visually differentiate editable versus non-editable cells?