From 2e5df0ff42c9ec542bd7c88de8f88f50a8ad5864 Mon Sep 17 00:00:00 2001 From: etimberg Date: Sat, 12 Nov 2016 22:38:25 -0500 Subject: [PATCH] Allow updating the config of a chart at runtime --- src/core/core.controller.js | 23 +++++++++++++ src/core/core.legend.js | 35 +++++++++++++++----- src/core/core.title.js | 43 +++++++++++++++--------- test/core.controller.tests.js | 49 +++++++++++++++++++++++++++ test/core.legend.tests.js | 62 +++++++++++++++++++++++++++++++++++ test/core.title.tests.js | 62 +++++++++++++++++++++++++++++++++++ 6 files changed, 251 insertions(+), 23 deletions(-) diff --git a/src/core/core.controller.js b/src/core/core.controller.js index bdc98ee04..ebbd081af 100644 --- a/src/core/core.controller.js +++ b/src/core/core.controller.js @@ -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 diff --git a/src/core/core.legend.js b/src/core/core.legend.js index 07349fd80..4ab51ba5d 100644 --- a/src/core/core.legend.js +++ b/src/core/core.legend.js @@ -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; } } }); diff --git a/src/core/core.title.js b/src/core/core.title.js index a7663258a..5b2d989f8 100644 --- a/src/core/core.title.js +++ b/src/core/core.title.js @@ -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) { + createNewTitleBlockAndAttach(chartInstance, titleOpts); + } + }, + beforeUpdate: function(chartInstance) { + var titleOpts = chartInstance.options.title; if (titleOpts) { - chartInstance.titleBlock = new Chart.Title({ - ctx: chartInstance.chart.ctx, - options: titleOpts, - chart: chartInstance - }); + titleOpts = helpers.configMerge(Chart.defaults.global.title, titleOpts); - Chart.layoutService.addBox(chartInstance, chartInstance.titleBlock); + if (chartInstance.titleBlock) { + chartInstance.titleBlock.options = titleOpts; + } else { + createNewTitleBlockAndAttach(chartInstance, titleOpts); + } + } else { + Chart.layoutService.removeBox(chartInstance, chartInstance.titleBlock); + delete chartInstance.titleBlock; } } }); diff --git a/test/core.controller.tests.js b/test/core.controller.tests.js index cc99b0d7f..44e7d3d21 100644 --- a/test/core.controller.tests.js +++ b/test/core.controller.tests.js @@ -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)); + }); + }); }); diff --git a/test/core.legend.tests.js b/test/core.legend.tests.js index 3cdeddc90..109a209e0 100644 --- a/test/core.legend.tests.js +++ b/test/core.legend.tests.js @@ -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)); + }); + }); }); diff --git a/test/core.title.tests.js b/test/core.title.tests.js index 704605278..f334a1c6c 100644 --- a/test/core.title.tests.js +++ b/test/core.title.tests.js @@ -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)); + }); + }); }); -- 2.47.3