Thanks to this tutorial I managed to create a KnockOut binding handler for Google's DataTable.
This is my binding handler, so far:
ko.bindingHandlers.dataTable = {
init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
var table = new google.visualization.Table(element);
ko.utils.domData.set(element, "dataTable", table);
},
update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
var value = ko.unwrap(valueAccessor());
// Get options:
var options = allBindings.get("tableOptions") || {};
// Default options:
options.width = options.width || "200px";
options.height = options.height || "200px";
options.showRowNumber = options.showRowNumber || false;
// Get events:
var onSelected = allBindings.get("select") || false;
if (onSelected) {
$(element).on("select", function(event, ui) {
valueAccessor()(ui.value);
});
}
var table = ko.utils.domData.get(element, "dataTable");
table.draw(value, options);
}
};
This is my HTML part:
<div data-bind="dataTable: $root.getData(), tableOptions: {width: '100%',height: '200px', 'allowHtml': true, 'cssClassNames': {'selectedTableRow': 'orange-background'} }"></div>
So far I get a table with fixed headers which works just fine.
Now I want to extent to binding handler to react on the 'select row' event.
I tried this using the // Get events
section in my handler but this is not working.
In my HTML I add select: $root.selectedRow(),
In my function selectedRow()
I put a console.log("In selectedRow")
. When I load the page I see selectedRow
is called for every row, but when I click on a row it is not called.
The row its background is changed to orange, so Google is adding the selectedTableRow
class.
How to wrap/bind to the select event?
The main thing that's going wrong is, if I'm not mistaken, the way you're trying to attach your event listener.
Your $(element).on("select", onSelect)
is not how the library you're using attaches event listeners. In the documentation you can see that you actually need to use: google.visualization.events.addListener(table, 'select', selectHandler);
Additionally, it's better to attach the event listener in the init
method. update
is called whenever your data changes, so it might add multiple event listeners.
Here's a working example of your code:
google.charts.load('current', {
'packages': ['table']
});
google.charts.setOnLoadCallback(function() {
ko.bindingHandlers.dataTable = {
init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
var table = new google.visualization.Table(element);
ko.utils.domData.set(element, "dataTable", table);
// Get events:
var onSelected = allBindings.get("select") || false;
if (onSelected) {
google.visualization.events.addListener(table, 'select', function() {
// TODO: null/undefined/multiple selection checks
var data = valueAccessor();
var row = table.getSelection()[0].row;
onSelected(data.getValue(row, 1)); // Sends salary of clicked row
});
}
},
update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
var value = ko.unwrap(valueAccessor());
// Get options:
var options = allBindings.get("tableOptions") || {};
// Default options:
options.width = options.width || "200px";
options.height = options.height || "200px";
options.showRowNumber = options.showRowNumber || false;
var table = ko.utils.domData.get(element, "dataTable");
table.draw(value, options);
}
};
ko.applyBindings({
onSelect: function(value) {
alert(value);
},
getData: function() {
var data = new google.visualization.DataTable();
data.addColumn('string', 'Name');
data.addColumn('number', 'Salary');
data.addColumn('boolean', 'Full Time Employee');
data.addRows([
['Mike', {
v: 10000,
f: '$10,000'
},
true
],
['Jim', {
v: 8000,
f: '$8,000'
},
false
],
['Alice', {
v: 12500,
f: '$12,500'
},
true
],
['Bob', {
v: 7000,
f: '$7,000'
},
true
]
]);
return data;
}
})
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<div data-bind="dataTable: getData(), tableOptions: {width: '100%',height: '200px', 'allowHtml': true, 'cssClassNames': {'selectedTableRow': 'orange-background'} }, select: onSelect"></div>