]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Fix scale options update (#4198)
authorXingan Wang <wangxgwxg@gmail.com>
Wed, 29 Nov 2017 21:52:23 +0000 (13:52 -0800)
committerSimon Brunel <simonbrunel@users.noreply.github.com>
Wed, 29 Nov 2017 21:52:23 +0000 (22:52 +0100)
- allow options to be updated in-place or as a new object
- re-merge new options and rebuild scales & tooltips
- preserve reference to old scale if id/type not changed
- related tests and new sample also added.
- update document about options update
- update doc and example

docs/developers/updates.md
samples/samples.js
samples/scales/toggle-scale-type.html [new file with mode: 0644]
src/core/core.controller.js
src/core/core.datasetController.js
test/specs/core.controller.tests.js

index 06550ce0cecf2b4e854f48a954421d7d05eb9597..b65757f856b9eefc4412a4c777d5a26a5a6141a5 100644 (file)
@@ -1,6 +1,6 @@
 # Updating Charts
 
-It's pretty common to want to update charts after they've been created. When the chart data is changed, Chart.js will animate to the new data values. 
+It's pretty common to want to update charts after they've been created. When the chart data or options are changed, Chart.js will animate to the new data values and options.
 
 ## Adding or Removing Data
 
@@ -14,9 +14,7 @@ function addData(chart, label, data) {
     });
     chart.update();
 }
-```
 
-```javascript
 function removeData(chart) {
     chart.data.labels.pop();
     chart.data.datasets.forEach((dataset) => {
@@ -26,6 +24,78 @@ function removeData(chart) {
 }
 ```
 
+## Updating Options
+
+To update the options, mutating the options property in place or passing in a new options object are supported.
+
+- If the options are mutated in place, other option properties would be preserved, including those calculated by Chart.js.
+- If created as a new object, it would be like creating a new chart with the options - old options would be discarded.
+
+```javascript
+function updateConfigByMutating(chart) {
+    chart.options.title.text = 'new title';
+    chart.update();
+}
+
+function updateConfigAsNewObject(chart) {
+    chart.options = {
+        responsive: true,
+        title:{
+            display:true,
+            text: 'Chart.js'
+        },
+        scales: {
+            xAxes: [{
+                display: true
+            }],
+            yAxes: [{
+                display: true
+            }]
+        }
+    }
+    chart.update();
+}
+```
+
+Scales can be updated separately without changing other options.
+To update the scales, pass in an object containing all the customization including those unchanged ones.
+
+Variables referencing any one from `chart.scales` would be lost after updating scales with a new `id` or the changed `type`.
+
+```javascript
+function updateScales(chart) {
+    var xScale = chart.scales['x-axis-0'];
+    var yScale = chart.scales['y-axis-0'];
+    chart.options.scales = {
+        xAxes: [{
+            id: 'newId',
+            display: true
+        }],
+        yAxes: [{
+            display: true,
+            type: 'logarithmic'
+        }]
+    }
+    chart.update();
+    // need to update the reference
+    xScale = chart.scales['newId'];
+    yScale = chart.scales['y-axis-0'];
+}
+```
+
+You can also update a specific scale either by specifying its index or id.
+
+```javascript
+function updateScale(chart) {
+    chart.options.scales.yAxes[0] = {
+        type: 'logarithmic'
+    }
+    chart.update();
+}
+```
+
+Code sample for updating options can be found in [toggle-scale-type.html](../../samples/scales/toggle-scale-type.html).
+
 ## Preventing Animations
 
 Sometimes when a chart updates, you may not want an animation. To achieve this you can call `update` with a duration of `0`. This will render the chart synchronously and without an animation.
\ No newline at end of file
index 2d11e9d48c9e2aea5a0c5be1a6f8ad11e3a56bc9..b818827c6d24f510e95fb7e6c70aed0f83c1ad42 100644 (file)
                }, {
                        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 (file)
index 0000000..b46687e
--- /dev/null
@@ -0,0 +1,99 @@
+<!doctype html>
+<html>
+
+<head>
+    <title>Toggle Scale Type</title>
+    <script src="../../dist/Chart.bundle.js"></script>
+    <script src="../utils.js"></script>
+    <style>
+    canvas {
+        -moz-user-select: none;
+        -webkit-user-select: none;
+        -ms-user-select: none;
+    }
+    </style>
+</head>
+
+<body>
+    <div style="width:75%;">
+        <canvas id="canvas"></canvas>
+    </div>
+    <button id="toggleScale">Toggle Scale Type</button>
+    <script>
+    var randomScalingFactor = function() {
+        return Math.ceil(Math.random() * 10.0) * Math.pow(10, Math.ceil(Math.random() * 5));
+    };
+
+    var type = 'linear';
+
+    var config = {
+        type: 'line',
+        data: {
+            labels: ["January", "February", "March", "April", "May", "June", "July"],
+            datasets: [{
+                label: "My First dataset",
+                backgroundColor: window.chartColors.red,
+                borderColor: window.chartColors.red,
+                fill: false,
+                data: [
+                    randomScalingFactor(),
+                    randomScalingFactor(),
+                    randomScalingFactor(),
+                    randomScalingFactor(),
+                    randomScalingFactor(),
+                    randomScalingFactor(),
+                    randomScalingFactor()
+                ],
+            }, {
+                label: "My Second dataset",
+                backgroundColor: window.chartColors.blue,
+                borderColor: window.chartColors.blue,
+                fill: false,
+                data: [
+                    randomScalingFactor(),
+                    randomScalingFactor(),
+                    randomScalingFactor(),
+                    randomScalingFactor(),
+                    randomScalingFactor(),
+                    randomScalingFactor(),
+                    randomScalingFactor()
+                ],
+            }]
+        },
+        options: {
+            responsive: true,
+            title:{
+                display: true,
+                text: 'Chart.js Line Chart - ' + type
+            },
+            scales: {
+                xAxes: [{
+                    display: true,
+                }],
+                yAxes: [{
+                    display: true,
+                    type: type
+                }]
+            }
+        }
+    };
+
+    window.onload = function() {
+        var ctx = document.getElementById("canvas").getContext("2d");
+        window.myLine = new Chart(ctx, config);
+    };
+
+    document.getElementById('toggleScale').addEventListener('click', function() {
+        type = type === 'linear' ? 'logarithmic' : 'linear';
+        window.myLine.options.title.text = 'Chart.js Line Chart - ' + type;
+        window.myLine.options.scales.yAxes[0] = {
+            display: true,
+            type: type
+        }
+
+        window.myLine.update();
+    });
+    </script>
+</body>
+
+</html>
index 5fd4b2d6aa6d433f94f6fefae8a3a99cd897c794..17e6c3bfa6764c902a1f0edd2e3e3ed44635531c 100644 (file)
@@ -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) {
index 67dbe27f20097c5e0a6c641649c3d922b8b7ddaf..ee6158c35b1e9953ca399f558d1fe1cd2805d2da 100644 (file)
@@ -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;
                        }
                },
index b2615f14eec78bb7ff2199760b9d618ae4daf885..1ca699bc13254497c54827c8bb2e465dca1616c5 100644 (file)
@@ -775,6 +775,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',
@@ -798,6 +830,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',