-
-
Save jmhdez/4987b053e817d65d7c68 to your computer and use it in GitHub Desktop.
/*global ko, Chart */ | |
(function(ko, Chart) { | |
ko.bindingHandlers.chartType = { | |
init: function(element, valueAccessor, allBindings, viewModel, bindingContext) { | |
if (!allBindings.has('chartData')) { | |
throw Error('chartType must be used in conjunction with chartData and (optionally) chartOptions'); | |
} | |
}, | |
update: function (element, valueAccessor, allBindings, viewModel, bindingContext) { | |
var ctx = element.getContext('2d'), | |
type = ko.unwrap(valueAccessor()), | |
data = ko.unwrap(allBindings.get('chartData')), | |
options = ko.unwrap(allBindings.get('chartOptions')) || {}; | |
if (this.chart) { | |
this.chart.destroy(); | |
delete this.chart; | |
} | |
this.chart = new Chart(ctx)[type](data, options); | |
} | |
}; | |
ko.bindingHandlers.chartData = { | |
init: function(element, valueAccessor, allBindings, viewModel, bindingContext) { | |
if (!allBindings.has('chartType')) { | |
throw Error('chartData must be used in conjunction with chartType and (optionally) chartOptions'); | |
} | |
} | |
}; | |
ko.bindingHandlers.chartOptions = { | |
init: function(element, valueAccessor, allBindings, viewModel, bindingContext) { | |
if (!allBindings.has('chartData') || !allBindings.has('chartType')) { | |
throw Error('chartOptions must be used in conjunction with chartType and chartData'); | |
} | |
} | |
}; | |
})(ko, Chart); |
Found small bug with latest knockout:
if (this.chart) {
this.chart.destroy();
delete this.chart;
}
this.chart = new Chart(ctx)[type](data, options);
should be replaced with
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
$(element).chart.destroy();
delete $(element).chart;
});
$(element).chart = new Chart(ctx)[type](data, options);
also as an example:
<canvas width="200" height="200" data-bind="chartType: 'Doughnut',
chartData: Chart_ActiveMachines,
chartOptions : {
animation : false,
showTooltips: false,
percentageInnerCutout : 60,
segmentShowStroke : false
}
"></canvas>
I have extended this to support click events on Pie and Doughnut charts (via segmentClick binding), Line charts (via lineClick), and Bar charts (via barClick), as well as the fix noted above, which you can see working here:
https://jsfiddle.net/glaivier/kywm94dv/7/
Some examples of usage are as follows:
<canvas width="200" height="200" data-bind="chartType: 'Doughnut',
segmentClick: alertLabel,
chartData: machines,
chartOptions : {
animation : true,
showTooltips: true,
percentageInnerCutout : 50,
segmentShowStroke : true,
segmentStrokeColor: '#222222'
}
"></canvas>
<canvas width="300" height="150" data-bind="chartType: 'Bar',
barClick: alertJson,
chartData: browserData,
chartOptions : {
animation : true,
showTooltips: true,
segmentShowStroke : true,
segmentStrokeColor: '#222222'
}
"></canvas>
<canvas width="300" height="150" data-bind="chartType: 'Line',
lineClick: alertJson,
chartData: lineData,
chartOptions : {
animation : true,
showTooltips: true,
segmentShowStroke : true,
segmentStrokeColor: '#222222'"></canvas>
..... Use this with models such as these:
function KoModel(data) {
var self = this;
self.machines = ko.observableArray(data.machines);
self.browserData = ko.observable(data.browsers || {});
self.lineData = ko.observable(data.lineData || {});
self.alertLabel = function (chartItem, chart) {
alert(chartItem.label);
};
self.alertJson = function (chartItem, chart) {
alert(JSON.stringify(chartItem));
};
};
$(document).ready(function () {
var data = {
machines: [{
value: 30,
color: '#FF3333',
highlight: '#FF7777',
label: 'PC'
}, {
value: 85,
color: '#3333FF',
highlight: '#7777FF',
label: 'Android'
}, {
value: 15,
color: '#33FF33',
highlight: '#77FF77',
label: 'Linux'
}],
browserData: {
labels: ["September", "October", "November", "Dcember"],
datasets: [{
label: 'IE',
fillColor: 'rgba(250, 50, 50, 0.5)',
strokeColor: 'rgba(250, 50, 50, 0.8)',
highlightFill: 'rgba(250, 50, 50, 0.75)',
highlightStroke: 'rgba(250, 50, 50, 1)',
data: [60, 50, 45, 32]
}, {
label: 'Firefox',
fillColor: 'rgba(50, 50, 250, 0.5)',
strokeColor: 'rgba(50, 50, 250, 0.8)',
highlightFill: 'rgba(50, 50, 250, 0.75)',
highlightStroke: 'rgba(50, 50, 250, 1)',
data: [25, 22, 15, 24]
}, {
label: 'Chrome',
fillColor: 'rgba(50, 250, 50, 0.5)',
strokeColor: 'rgba(50, 250, 50, 0.8)',
highlightFill: 'rgba(50, 250, 50, 0.75)',
highlightstroke: 'rgba(50, 250, 50, 1)',
data: [10, 12, 16, 30]
}]
},
lineData: {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [{
label: "My First dataset",
fillColor: "rgba(220,120,220,0.2)",
strokeColor: "rgba(220,120,220,1)",
pointColor: "rgba(220,120,220,1)",
pointStrokeColor: "#fff",
pointHighlightFill: "#fff",
pointHighlightStroke: "rgba(220,220,220,1)",
data: [75, 69, 90, 91, 66, 65, 50]
}, {
label: "My Second dataset",
fillColor: "rgba(251,87,105,0.2)",
strokeColor: "rgba(251,87,105,1)",
pointColor: "rgba(251,87,105,1)",
pointStrokeColor: "#fff",
pointHighlightFill: "#fff",
pointHighlightStroke: "rgba(151,187,205,1)",
data: [38, 78, 20, 69, 26, 47, 10]
}, {
label: "Thurd dataset",
fillColor: "rgba(45,217,125,0.2)",
strokeColor: "rgba(45,217,125,1)",
pointColor: "rgba(45,217,125,1)",
pointStrokeColor: "#fff",
pointHighlightFill: "#fff",
pointHighlightStroke: "rgba(151,187,205,1)",
data: [18, 28, 50, 32, 66, 57, 86]
}]
}
};
var koModel = new KoModel(data);
ko.applyBindings(koModel);
});
The full JS file is as follows:
/*global ko, Chart */
// knockout-chartjs binding by James Burton (extended from example by jmhdez on GitHubGist to include fix and click events)
// Example: https://jsfiddle.net/glaivier/kywm94dv/7/
(function (ko, Chart) {
ko.bindingHandlers.barClick = {
init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
if (!allBindings.has('chartData')) {
throw Error('chartType must be used in conjunction with chartData and (optionally) chartOptions');
return;
}
var chartType = allBindings.get('chartType');
if (chartType !== 'Bar') {
throw Error('barClick can only be used with chartType Bar');
return;
}
},
update: function (element, valueAccesor, allBindings, viewModel, bindingContext) { }
};
ko.bindingHandlers.lineClick = {
init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
if (!allBindings.has('chartData')) {
throw Error('chartType must be used in conjunction with chartData and (optionally) chartOptions');
return;
}
var chartType = allBindings.get('chartType');
if (chartType !== 'Line') {
throw Error('lineClick can only be used with chartType Line');
return;
}
},
update: function (element, valueAccesor, allBindings, viewModel, bindingContext) { }
};
ko.bindingHandlers.segmentClick = {
init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
if (!allBindings.has('chartData')) {
throw Error('chartType must be used in conjunction with chartData and (optionally) chartOptions');
return;
}
var chartType = allBindings.get('chartType');
if (chartType !== 'Pie' && chartType !== 'Doughnut') {
throw Error('segmentClick can only be used with chartType Pie or Donut');
return;
}
},
update: function (element, valueAccesor, allBindings, viewModel, bindingContext) { }
};
ko.bindingHandlers.chartType = {
init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
if (!allBindings.has('chartData')) {
throw Error('chartType must be used in conjunction with chartData and (optionally) chartOptions');
}
},
update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
var ctx = element.getContext('2d'),
type = ko.unwrap(valueAccessor()),
data = ko.unwrap(allBindings.get('chartData')),
options = ko.unwrap(allBindings.get('chartOptions')) || {},
segmentClick = ko.unwrap(allBindings.get('segmentClick')),
barClick = ko.unwrap(allBindings.get('barClick')),
lineClick = ko.unwrap(allBindings.get('lineClick'));
/* NB: Fix for newer knockout (see https://gist.github.com/jmhdez/4987b053e817d65d7c68)
if (this.chart) {
this.chart.destroy();
delete this.chart;
}
this.chart = new Chart(ctx)[type](data, options);
//*/
ko.utils.domNodeDisposal.addDisposeCallback(element,
function () {
$(element).chart.destroy();
delete $(element).chart;
});
var newChart = new Chart(ctx)[type](data, options);
var $element = $(element)[0];
$element.chart = newChart;
//* End of fix
//* Remove existing click binding
if ($element.click) {
$element.removeEventListener('click', $element.click);
delete ($element.click);
}
//* Add segment click binding
switch (type) {
case "Pie":
case "Doughnut":
if (segmentClick) {
$element.click = function (evt) {
var activePoints = newChart.getSegmentsAtEvent(evt);
segmentClick(activePoints[0], newChart);
};
}
break;
case "Bar":
if (barClick) {
$element.click = function (evt) {
barClick(newChart.getBarsAtEvent(evt), newChart);
};
}
break;
case "Line":
if (lineClick) {
$element.click = function (evt) {
lineClick(newChart.getPointsAtEvent(evt), newChart);
};
}
break;
default:
break;
}
$element.addEventListener('click', $element.click);
}
};
ko.bindingHandlers.chartData = {
init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
if (!allBindings.has('chartType')) {
throw Error('chartData must be used in conjunction with chartType and (optionally) chartOptions');
}
}
};
ko.bindingHandlers.chartOptions = {
init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
if (!allBindings.has('chartData') || !allBindings.has('chartType')) {
throw Error('chartOptions must be used in conjunction with chartType and chartData');
}
}
};
})(ko, Chart);
Any option of using the same without defining the colours inside the datasets? I am intending to fetch data from back end services. So Defining colours at that level is a bit tricky because I won't have any idea about the dataset's size.
Do you have an example of how this would be used within your HTML markup?