Allow updating the config of a chart at runtime

This commit is contained in:
etimberg 2016-11-12 22:38:25 -05:00 committed by Evert Timberg
parent 7e5e29e3ee
commit 2e5df0ff42
6 changed files with 251 additions and 23 deletions

View File

@ -168,6 +168,26 @@ module.exports = function(Chart) {
return config;
}
/**
* Updates the config of the chart
* @param chart {Chart.Controller} chart to update the options for
*/
function updateConfig(chart) {
var newOptions = chart.options;
// Update Scale(s) with options
if (newOptions.scale) {
chart.scale.options = newOptions.scale;
} else if (newOptions.scales) {
newOptions.scales.xAxes.concat(newOptions.scales.yAxes).forEach(function(scaleOptions) {
chart.scales[scaleOptions.id].options = scaleOptions;
});
}
// Tooltip
chart.tooltip._options = newOptions.tooltips;
}
/**
* @class Chart.Controller
* The main controller of a chart.
@ -435,8 +455,11 @@ module.exports = function(Chart) {
this.tooltip.initialize();
},
update: function(animationDuration, lazy) {
var me = this;
updateConfig(me);
Chart.plugins.notify('beforeUpdate', [me]);
// In case the entire data object changed

View File

@ -489,20 +489,39 @@ module.exports = function(Chart) {
}
});
function createNewLegendAndAttach(chartInstance, legendOpts) {
var legend = new Chart.Legend({
ctx: chartInstance.chart.ctx,
options: legendOpts,
chart: chartInstance
});
chartInstance.legend = legend;
Chart.layoutService.addBox(chartInstance, legend);
}
// Register the legend plugin
Chart.plugins.register({
beforeInit: function(chartInstance) {
var opts = chartInstance.options;
var legendOpts = opts.legend;
var legendOpts = chartInstance.options.legend;
if (legendOpts) {
chartInstance.legend = new Chart.Legend({
ctx: chartInstance.chart.ctx,
options: legendOpts,
chart: chartInstance
});
createNewLegendAndAttach(chartInstance, legendOpts);
}
},
beforeUpdate: function(chartInstance) {
var legendOpts = chartInstance.options.legend;
Chart.layoutService.addBox(chartInstance, chartInstance.legend);
if (legendOpts) {
legendOpts = helpers.configMerge(Chart.defaults.global.legend, legendOpts);
if (chartInstance.legend) {
chartInstance.legend.options = legendOpts;
} else {
createNewLegendAndAttach(chartInstance, legendOpts);
}
} else {
Chart.layoutService.removeBox(chartInstance, chartInstance.legend);
delete chartInstance.legend;
}
}
});

View File

@ -22,7 +22,6 @@ module.exports = function(Chart) {
initialize: function(config) {
var me = this;
helpers.extend(me, config);
me.options = helpers.configMerge(Chart.defaults.global.title, config.options);
// Contains hit boxes for each dataset (in dataset order)
me.legendHitBoxes = [];
@ -30,12 +29,7 @@ module.exports = function(Chart) {
// These methods are ordered by lifecycle. Utilities then follow.
beforeUpdate: function() {
var chartOpts = this.chart.options;
if (chartOpts && chartOpts.title) {
this.options = helpers.configMerge(Chart.defaults.global.title, chartOpts.title);
}
},
beforeUpdate: noop,
update: function(maxWidth, maxHeight, margins) {
var me = this;
@ -187,20 +181,39 @@ module.exports = function(Chart) {
}
});
function createNewTitleBlockAndAttach(chartInstance, titleOpts) {
var title = new Chart.Title({
ctx: chartInstance.chart.ctx,
options: titleOpts,
chart: chartInstance
});
chartInstance.titleBlock = title;
Chart.layoutService.addBox(chartInstance, title);
}
// Register the title plugin
Chart.plugins.register({
beforeInit: function(chartInstance) {
var opts = chartInstance.options;
var titleOpts = opts.title;
var titleOpts = chartInstance.options.title;
if (titleOpts) {
chartInstance.titleBlock = new Chart.Title({
ctx: chartInstance.chart.ctx,
options: titleOpts,
chart: chartInstance
});
createNewTitleBlockAndAttach(chartInstance, titleOpts);
}
},
beforeUpdate: function(chartInstance) {
var titleOpts = chartInstance.options.title;
Chart.layoutService.addBox(chartInstance, chartInstance.titleBlock);
if (titleOpts) {
titleOpts = helpers.configMerge(Chart.defaults.global.title, titleOpts);
if (chartInstance.titleBlock) {
chartInstance.titleBlock.options = titleOpts;
} else {
createNewTitleBlockAndAttach(chartInstance, titleOpts);
}
} else {
Chart.layoutService.removeBox(chartInstance, chartInstance.titleBlock);
delete chartInstance.titleBlock;
}
}
});

View File

@ -830,4 +830,53 @@ describe('Chart.Controller', function() {
expect(meta.data[3]._model.y).toBe(484);
});
});
describe('config update', function() {
it ('should update scales options', function() {
var chart = acquireChart({
type: 'line',
data: {
labels: ['A', 'B', 'C', 'D'],
datasets: [{
data: [10, 20, 30, 100]
}]
},
options: {
responsive: true
}
});
chart.options.scales.yAxes[0].ticks.min = 0;
chart.options.scales.yAxes[0].ticks.max = 10;
chart.update();
var yScale = chart.scales['y-axis-0'];
expect(yScale.options.ticks.min).toBe(0);
expect(yScale.options.ticks.max).toBe(10);
});
it ('should update tooltip options', function() {
var chart = acquireChart({
type: 'line',
data: {
labels: ['A', 'B', 'C', 'D'],
datasets: [{
data: [10, 20, 30, 100]
}]
},
options: {
responsive: true
}
});
var newTooltipConfig = {
mode: 'dataset',
intersect: false
};
chart.options.tooltips = newTooltipConfig;
chart.update();
expect(chart.tooltip._options).toEqual(jasmine.objectContaining(newTooltipConfig));
});
});
});

View File

@ -367,4 +367,66 @@ describe('Legend block tests', function() {
"args": ["dataset3", 228, 132]
}]);*/
});
describe('config update', function() {
it ('should update the options', function() {
var chart = acquireChart({
type: 'line',
data: {
labels: ['A', 'B', 'C', 'D'],
datasets: [{
data: [10, 20, 30, 100]
}]
},
options: {
legend: {
display: true
}
}
});
expect(chart.legend.options.display).toBe(true);
chart.options.legend.display = false;
chart.update();
expect(chart.legend.options.display).toBe(false);
});
it ('should remove the legend if the new options are false', function() {
var chart = acquireChart({
type: 'line',
data: {
labels: ['A', 'B', 'C', 'D'],
datasets: [{
data: [10, 20, 30, 100]
}]
}
});
expect(chart.legend).not.toBe(undefined);
chart.options.legend = false;
chart.update();
expect(chart.legend).toBe(undefined);
});
it ('should create the legend if the legend options are changed to exist', function() {
var chart = acquireChart({
type: 'line',
data: {
labels: ['A', 'B', 'C', 'D'],
datasets: [{
data: [10, 20, 30, 100]
}]
},
options: {
legend: false
}
});
expect(chart.legend).toBe(undefined);
chart.options.legend = {};
chart.update();
expect(chart.legend).not.toBe(undefined);
expect(chart.legend.options).toEqual(jasmine.objectContaining(Chart.defaults.global.legend));
});
});
});

View File

@ -207,4 +207,66 @@ describe('Title block tests', function() {
args: []
}]);
});
describe('config update', function() {
it ('should update the options', function() {
var chart = acquireChart({
type: 'line',
data: {
labels: ['A', 'B', 'C', 'D'],
datasets: [{
data: [10, 20, 30, 100]
}]
},
options: {
title: {
display: true
}
}
});
expect(chart.titleBlock.options.display).toBe(true);
chart.options.title.display = false;
chart.update();
expect(chart.titleBlock.options.display).toBe(false);
});
it ('should remove the title if the new options are false', function() {
var chart = acquireChart({
type: 'line',
data: {
labels: ['A', 'B', 'C', 'D'],
datasets: [{
data: [10, 20, 30, 100]
}]
}
});
expect(chart.titleBlock).not.toBe(undefined);
chart.options.title = false;
chart.update();
expect(chart.titleBlock).toBe(undefined);
});
it ('should create the title if the title options are changed to exist', function() {
var chart = acquireChart({
type: 'line',
data: {
labels: ['A', 'B', 'C', 'D'],
datasets: [{
data: [10, 20, 30, 100]
}]
},
options: {
title: false
}
});
expect(chart.titleBlock).toBe(undefined);
chart.options.title = {};
chart.update();
expect(chart.titleBlock).not.toBe(undefined);
expect(chart.titleBlock.options).toEqual(jasmine.objectContaining(Chart.defaults.global.title));
});
});
});