diff --git a/samples/samples.js b/samples/samples.js index 235685bf2fa..d7e986c446a 100644 --- a/samples/samples.js +++ b/samples/samples.js @@ -133,6 +133,9 @@ }, { title: 'Non numeric Y Axis', path: 'scales/non-numeric-y.html' + }, { + title: 'Toggle Scale Type', + path: 'scales/toggle-scale-type.html' }] }, { title: 'Legend', diff --git a/samples/scales/toggle-scale-type.html b/samples/scales/toggle-scale-type.html new file mode 100644 index 00000000000..e6bcd25a843 --- /dev/null +++ b/samples/scales/toggle-scale-type.html @@ -0,0 +1,104 @@ + + + + + Toggle Scale Type + + + + + + +
+ +
+ + + + + diff --git a/src/core/core.controller.js b/src/core/core.controller.js index 28dd9765095..3e7cc3803b0 100644 --- a/src/core/core.controller.js +++ b/src/core/core.controller.js @@ -45,17 +45,21 @@ module.exports = function(Chart) { 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; - }); - } - + helpers.each(chart.scales, function(scale) { + Chart.layoutService.removeBox(chart, scale); + }); + + newOptions = helpers.configMerge( + Chart.defaults.global, + Chart.defaults[chart.config.type], + newOptions); + + chart.options = chart.config.options = newOptions; + chart.ensureScalesHaveIDs(); + chart.buildOrUpdateScales(); // Tooltip chart.tooltip._options = newOptions.tooltips; + chart.tooltip.initialize(); } function positionIsHorizontal(position) { @@ -143,7 +147,7 @@ module.exports = function(Chart) { // Make sure scales have IDs and are built before we build any controllers. me.ensureScalesHaveIDs(); - me.buildScales(); + me.buildOrUpdateScales(); me.initToolTip(); // After init plugin notification @@ -223,11 +227,15 @@ module.exports = function(Chart) { /** * Builds a map of scale ID to scale object for future lookup. */ - buildScales: function() { + buildOrUpdateScales: function() { var me = this; var options = me.options; - var scales = me.scales = {}; + var scales = me.scales || {}; var items = []; + var updated = Object.keys(scales).reduce(function(obj, id) { + obj[id] = false; + return obj; + }, {}); if (options.scales) { items = items.concat( @@ -251,24 +259,35 @@ module.exports = function(Chart) { helpers.each(items, function(item) { var scaleOptions = item.options; + var id = scaleOptions.id; var scaleType = helpers.valueOrDefault(scaleOptions.type, item.dtype); - var scaleClass = Chart.scaleService.getScaleConstructor(scaleType); - if (!scaleClass) { - return; - } if (positionIsHorizontal(scaleOptions.position) !== positionIsHorizontal(item.dposition)) { scaleOptions.position = item.dposition; } - var scale = new scaleClass({ - id: scaleOptions.id, - options: scaleOptions, - ctx: me.ctx, - chart: me - }); + updated[id] = true; + var scale = null; + if (id in scales && scales[id].type === scaleType) { + scale = scales[id]; + scale.options = scaleOptions; + scale.ctx = me.ctx; + scale.chart = me; + } else { + var scaleClass = Chart.scaleService.getScaleConstructor(scaleType); + if (!scaleClass) { + return; + } + scale = new scaleClass({ + id: id, + type: scaleType, + options: scaleOptions, + ctx: me.ctx, + chart: me + }); + scales[scale.id] = scale; + } - scales[scale.id] = scale; scale.mergeTicksOptions(); // TODO(SB): I think we should be able to remove this custom case (options.scale) @@ -278,6 +297,14 @@ module.exports = function(Chart) { me.scale = scale; } }); + // clear up discarded scales + helpers.each(updated, function(hasUpdated, id) { + if (!hasUpdated) { + delete scales[id]; + } + }); + + me.scales = scales; Chart.scaleService.addScalesToLayout(this); }, @@ -301,6 +328,7 @@ module.exports = function(Chart) { if (meta.controller) { meta.controller.updateIndex(datasetIndex); + meta.controller.linkScales(); } else { var ControllerClass = Chart.controllers[meta.type]; if (ControllerClass === undefined) { diff --git a/src/core/core.datasetController.js b/src/core/core.datasetController.js index 67dbe27f200..ee6158c35b1 100644 --- a/src/core/core.datasetController.js +++ b/src/core/core.datasetController.js @@ -111,10 +111,10 @@ module.exports = function(Chart) { var meta = me.getMeta(); var dataset = me.getDataset(); - if (meta.xAxisID === null) { + if (meta.xAxisID === null || !(meta.xAxisID in me.chart.scales)) { meta.xAxisID = dataset.xAxisID || me.chart.options.scales.xAxes[0].id; } - if (meta.yAxisID === null) { + if (meta.yAxisID === null || !(meta.yAxisID in me.chart.scales)) { meta.yAxisID = dataset.yAxisID || me.chart.options.scales.yAxes[0].id; } }, diff --git a/test/specs/core.controller.tests.js b/test/specs/core.controller.tests.js index beac21e0daa..3bb7c68952b 100644 --- a/test/specs/core.controller.tests.js +++ b/test/specs/core.controller.tests.js @@ -736,6 +736,38 @@ describe('Chart', function() { }); describe('config update', function() { + it ('should update options', function() { + var chart = acquireChart({ + type: 'line', + data: { + labels: ['A', 'B', 'C', 'D'], + datasets: [{ + data: [10, 20, 30, 100] + }] + }, + options: { + responsive: true + } + }); + + chart.options = { + responsive: false, + scales: { + yAxes: [{ + ticks: { + min: 0, + 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 scales options', function() { var chart = acquireChart({ type: 'line', @@ -759,6 +791,79 @@ describe('Chart', function() { expect(yScale.options.ticks.max).toBe(10); }); + it ('should update scales options from new object', function() { + var chart = acquireChart({ + type: 'line', + data: { + labels: ['A', 'B', 'C', 'D'], + datasets: [{ + data: [10, 20, 30, 100] + }] + }, + options: { + responsive: true + } + }); + + var newScalesConfig = { + yAxes: [{ + ticks: { + min: 0, + max: 10 + } + }] + }; + chart.options.scales = newScalesConfig; + + 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 remove discarded scale', function() { + var chart = acquireChart({ + type: 'line', + data: { + labels: ['A', 'B', 'C', 'D'], + datasets: [{ + data: [10, 20, 30, 100] + }] + }, + options: { + responsive: true, + scales: { + yAxes: [{ + id: 'yAxis0', + ticks: { + min: 0, + max: 10 + } + }] + } + } + }); + + var newScalesConfig = { + yAxes: [{ + ticks: { + min: 0, + max: 10 + } + }] + }; + chart.options.scales = newScalesConfig; + + chart.update(); + + var yScale = chart.scales.yAxis0; + expect(yScale).toBeUndefined(); + var newyScale = chart.scales['y-axis-0']; + expect(newyScale.options.ticks.min).toBe(0); + expect(newyScale.options.ticks.max).toBe(10); + }); + it ('should update tooltip options', function() { var chart = acquireChart({ type: 'line',