Skip to content

Commit

Permalink
Dynamic axes (#595)
Browse files Browse the repository at this point in the history
  • Loading branch information
pablojim committed May 14, 2017
1 parent bdfc67c commit a517b2a
Show file tree
Hide file tree
Showing 8 changed files with 350 additions and 102 deletions.
127 changes: 89 additions & 38 deletions dist/highcharts-ng.js
@@ -1,6 +1,6 @@
/**
* highcharts-ng
* @version v1.1.1-dev - 2017-05-09
* @version v1.1.1-dev - 2017-05-14
* @link https://github.com/pablojim/highcharts-ng
* @author Barry Fitzgerald <>
* @license MIT License, http://www.opensource.org/licenses/MIT
Expand All @@ -26,23 +26,25 @@ if (typeof module !== 'undefined' && typeof exports !== 'undefined' && module.ex

angular.module('highcharts-ng', [])
.component('highchart', {
bindings: {
config: '<',
changeDetection: '<'
},
controller: HighChartNGController
bindings: {
config: '<',
changeDetection: '<'
},
controller: HighChartNGController
});

HighChartNGController.$inject = ['$element', '$timeout'];

function HighChartNGController($element, $timeout) {
var seriesId = 0;
var yAxisId = 0;
var xAxisId = 0;
var ctrl = this;
var prevConfig = {};
var mergedConfig = {};
var detector = ctrl.changeDetection || angular.equals;
this.$onInit = function() {
ctrl.config.getChartObj = function(){
this.$onInit = function () {
ctrl.config.getChartObj = function () {
return ctrl.chart;
};
prevConfig = angular.merge({}, ctrl.config);
Expand All @@ -53,51 +55,93 @@ if (typeof module !== 'undefined' && typeof exports !== 'undefined' && module.ex
// https://github.com/pablojim/highcharts-ng/issues/550
var originalWidth = $element[0].clientWidth;
var originalHeight = $element[0].clientHeight;
$timeout(function() {
$timeout(function () {
if ($element[0].clientWidth !== originalWidth || $element[0].clientHeight !== originalHeight) {
ctrl.chart.reflow();
}
}, 0, false);
};

this.$doCheck = function() {
if(!detector(ctrl.config, prevConfig)) {
this.removeItems = function (newItems, chartItems, id, toIgnore) {
if (newItems && Array.isArray(newItems)) {
var ids = ensureIds(newItems, id);
for (var i = chartItems.length - 1; i >= 0; i -= 1) {
var a = chartItems[i];
if ((toIgnore.indexOf(a.options.id) < 0) && (ids.indexOf(a.options.id) < 0)) {
//if we don't set redraw to true, it can create
//glitches in the chart's rendering where the series
//doesn't completely re-render
a.remove(true);
}
}
}

};

this.removeUnlinkedObjects = function (mergedConfig) {
/*
Removes unlinked objects, items that have been removed in the config,
but not yet removed from the HighChart object
*/
//First check to see if there are any axes that need to be removed
//If a series is linked to the axis, it will be removed by HighCharts
this.removeItems(mergedConfig.yAxis, ctrl.chart.yAxis, yAxisId, 'navigator-y-axis');
this.removeItems(mergedConfig.xAxis, ctrl.chart.xAxis, xAxisId, 'navigator-x-axis');
this.removeItems(mergedConfig.series, ctrl.chart.series, seriesId, 'highcharts-navigator-series');
//TODO do we need to handle removing series from the config that highcharts has removed as part
//of removing axes?
};

this.addAnyNewAxes = function (configAxes, chart, isX) {
if (configAxes && Array.isArray(configAxes)) {
angular.forEach(configAxes, function (s) {
if (!chart.get(s.id)) {
chart.addAxis(s, isX);
}
});
}
};

this.$doCheck = function () {
if (!detector(ctrl.config, prevConfig)) {
prevConfig = angular.merge({}, ctrl.config);
mergedConfig = getMergedOptions($element, ctrl.config, seriesId);
var ids = ensureIds(mergedConfig.series, seriesId);

//Remove any unlinked objects before adding
this.removeUnlinkedObjects(mergedConfig);

//Allows dynamic adding Axes
this.addAnyNewAxes(mergedConfig.yAxis, ctrl.chart, false);
this.addAnyNewAxes(mergedConfig.xAxis, ctrl.chart, true);

//Allows dynamic adding of series
if (mergedConfig.series) {
//Remove any missing series
for (var i = ctrl.chart.series.length - 1; i >= 0; i--) {
var s = ctrl.chart.series[i];
if (s.options.id !== 'highcharts-navigator-series' && ids.indexOf(s.options.id) < 0) {
s.remove(false);
}
}
// Add any new series
angular.forEach(ctrl.config.series, function(s) {
angular.forEach(ctrl.config.series, function (s) {
if (!ctrl.chart.get(s.id)) {
ctrl.chart.addSeries(s);
}
});
}

ctrl.chart.update(mergedConfig, true);
}
};

this.$onDestroy = function() {
if (ctrl.chart) {
try{
ctrl.chart.destroy();
}catch(ex){
// fail silently as highcharts will throw exception if element doesn't exist
}

$timeout(function(){
$element.remove();
}, 0);
this.$onDestroy = function () {
if (ctrl.chart) {
try {
ctrl.chart.destroy();
} catch (ex) {
// fail silently as highcharts will throw exception if element doesn't exist
}
};
}

$timeout(function () {
$element.remove();
}, 0);
}
};
}

function getMergedOptions(element, config, seriesId) {
var mergedOptions = {};
Expand All @@ -116,7 +160,7 @@ if (typeof module !== 'undefined' && typeof exports !== 'undefined' && module.ex

if (config) {
//check all series and axis ids are set
if(config.series) {
if (config.series) {
ensureIds(config.series, seriesId);
}

Expand All @@ -132,7 +176,7 @@ if (typeof module !== 'undefined' && typeof exports !== 'undefined' && module.ex

var chartTypeMap = {
'stock': 'StockChart',
'map': 'Map',
'map': 'Map',
'chart': 'Chart'
};

Expand All @@ -141,15 +185,22 @@ if (typeof module !== 'undefined' && typeof exports !== 'undefined' && module.ex
return chartTypeMap[('' + config.chartType).toLowerCase()];
}

function ensureIds(series, seriesId) {
function ensureIds(chartCollection, collectionId) {
/*
Ensures each item in the iteratble chartCollection has an id,
and if not auto-generates one incrementing collectionId
*/
var ids = [];
angular.forEach(series, function(s) {
angular.forEach(chartCollection, function (s) {
if (!angular.isDefined(s.id)) {
s.id = 'series-' + seriesId++;
collectionId += 1;
s.id = 'cc-' + collectionId;
}
ids.push(s.id);
});

return ids;
}


}());
4 changes: 2 additions & 2 deletions dist/highcharts-ng.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

48 changes: 42 additions & 6 deletions example/charts/app.js
Expand Up @@ -45,11 +45,47 @@ myapp.controller('myctrl', function ($scope) {
$scope.addPoints = function () {
var seriesArray = $scope.chartConfig.series;
var rndIdx = Math.floor(Math.random() * seriesArray.length);
seriesArray[rndIdx].data = seriesArray[rndIdx].data.concat([1, 10, 20])
seriesArray[rndIdx].data = seriesArray[rndIdx].data.concat([1, 10, 20]);
};


var seriesId = 0;
var yAxisId = 0;
var xAxisId = 0;

$scope.addAxis = function(xy) {
/*
Adds a Y Axis
*/
var id;
var axis;
if (xy==='Y') {
yAxisId += 1;
id = yAxisId;
axis = 'yAxis';
} else {
xAxisId += 1;
id = xAxisId;
axis = 'xAxis';
}


var rnd = [];
for (var i = 0; i < 10; i++) {
rnd.push(Math.floor(Math.random() * 20) + 1);
}
if (!$scope.chartConfig[axis]) {
$scope.chartConfig[axis] = [];
}
$scope.chartConfig[axis].push({
min: Math.min.apply(null, rnd),
max: Math.max.apply(null, rnd),
title: {
text: xy + "-Axis" + id.toString()
},
id: xy + 'Axis_' + id
});
};


$scope.addSeries = function () {
var rnd = []
Expand All @@ -61,22 +97,22 @@ myapp.controller('myctrl', function ($scope) {
data: rnd,
id: sId
});
}
};

$scope.removeRandomSeries = function () {
var seriesArray = $scope.chartConfig.series;
var rndIdx = Math.floor(Math.random() * seriesArray.length);
seriesArray.splice(rndIdx, 1);
}
};

$scope.removeSeries = function (id) {
var seriesArray = $scope.chartConfig.series;
seriesArray.splice(id, 1);
}
};

$scope.toggleHighCharts = function () {
this.chartConfig.useHighStocks = !this.chartConfig.useHighStocks;
}
};

$scope.replaceAllSeries = function () {
var data = [
Expand Down
41 changes: 22 additions & 19 deletions example/charts/general-example.html
Expand Up @@ -34,17 +34,26 @@ <h2>Highcharts-ng example</h2>
<div class="row-fluid">{{chartConfig.getChartObj().chartHeight}}</div>
</div>
<div class="span4">
<div class="row-fluid">Title <input ng-model="chartConfig.title.text"></div>
<div class="row-fluid">Subtitle <input ng-model="chartConfig.subtitle.text"></div>
<div class="row-fluid">Width <input type="number" ng-model="chartConfig.chart.width"></div>
<div class="row-fluid">Height <input type="number" ng-model="chartConfig.chart.height"></div>
<div class="row-fluid">Default Type <select ng-model="chartConfig.chart.type" ng-options="t.id as t.title for t in chartTypes"></select></div>
<div class="row-fluid">Stack <select ng-model="chartConfig.plotOptions.series.stacking" ng-options="t.id as t.title for t in chartStack"></select></div>
<div class="row-fluid"><label><input type="checkbox" ng-model="chartConfig.loading"> is loading</label></div>
<div class="row-fluid"><label><input type="checkbox" ng-model="chartConfig.credits.enabled"> credits</label></div>
<h4>Series</h4>
<div class="row-fluid" ng-repeat="ser in chartSeries">
<div class="span12 well">
<div class="row-fluid">Title <input ng-model="chartConfig.title.text"></div>
<div class="row-fluid">Subtitle <input ng-model="chartConfig.subtitle.text"></div>
<div class="row-fluid">Width <input type="number" ng-model="chartConfig.chart.width"></div>
<div class="row-fluid">Height <input type="number" ng-model="chartConfig.chart.height"></div>
<div class="row-fluid">Default Type <select ng-model="chartConfig.chart.type" ng-options="t.id as t.title for t in chartTypes"></select></div>
<div class="row-fluid">Stack <select ng-model="chartConfig.plotOptions.series.stacking" ng-options="t.id as t.title for t in chartStack"></select></div>
<div class="row-fluid"><label><input type="checkbox" ng-model="chartConfig.loading"> is loading</label></div>
<div class="row-fluid"><label><input type="checkbox" ng-model="chartConfig.credits.enabled"> credits</label></div>
<div class="row-fluid"><button ng-click="addSeries()">Add Series</button></div>
<div class="row-fluid"><button ng-click="addPoints()">Add Points to Random Series</button></div>
<div class="row-fluid"><button ng-click="removeRandomSeries()">Remove Random Series</button></div>
<div class="row-fluid"><button ng-click="toggleHighCharts()">HighChart/HighStock</button></div>
<div class="row-fluid"><button ng-click="replaceAllSeries()">Replace all series</button></div>
<div class="row-fluid"><button ng-click="addAxis('x')">Add X Axis</button></div>
<div class="row-fluid"><button ng-click="addAxis('y')">Add Y Axis</button></div>
<div class="row-fluid">Min: <input type="number" ng-model="chartConfig.xAxis.currentMin"></div>
<div class="row-fluid">Max: <input type="number" ng-model="chartConfig.xAxis.currentMax"></div>
<h4>Series</h4>
<div class="row-fluid" ng-repeat="ser in chartSeries">
<div class="span12 well">
<div class="row-fluid">Title <input ng-model="ser.name"></div>
<div class="row-fluid">Type <select ng-model="ser.type" ng-options="t.id as t.title for t in chartTypes"></select></div>
<div class="row-fluid">Color <input ng-model="ser.color"></div>
Expand All @@ -53,14 +62,8 @@ <h4>Series</h4>
<div class="row-fluid"><label><input type="checkbox" ng-model="ser.connectNulls"> interpolate</label></div>
<div class="row-fluid"><button ng-click="removeSeries($index)">Delete</button></div>
</div>
</div>
<div class="row-fluid"><button ng-click="addSeries()">Add Series</button></div>
<div class="row-fluid"><button ng-click="addPoints()">Add Points to Random Series</button></div>
<div class="row-fluid"><button ng-click="removeRandomSeries()">Remove Random Series</button></div>
<div class="row-fluid"><button ng-click="toggleHighCharts()">HighChart/HighStock</button></div>
<div class="row-fluid"><button ng-click="replaceAllSeries()">Replace all series</button></div>
<div class="row-fluid">Min: <input type="number" ng-model="chartConfig.xAxis.currentMin"></div>
<div class="row-fluid">Max: <input type="number" ng-model="chartConfig.xAxis.currentMax"></div>
</div>

</div>
</div>
</div>
Expand Down
7 changes: 7 additions & 0 deletions jsfiddles/dynamic_axis/demo.details
@@ -0,0 +1,7 @@
---
name: Basic Highcharts-ng demo
description: Dynamic axis Highcharts-ng demo
authors:
- yoshi9143
normalize_css: no
...
20 changes: 20 additions & 0 deletions jsfiddles/dynamic_axis/demo.html
@@ -0,0 +1,20 @@
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.js"></script>
<script src="https://code.highcharts.com/stock/highstock.src.js"></script>
<script src="https://rawgithub.com/pablojim/highcharts-ng/master/src/highcharts-ng.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">

<div ng-app="myapp">
<div ng-controller="myctrl">
<div class="span12">
<input ng-model="chartConfig.title.text">
<button ng-click="addSeries()">Add Series with new Y-Axis</button>
<button ng-click="addPoints()">Add Points to Random Series</button>
<button ng-click="removeRandomSeries()">Remove Random Series</button>
</div>
<div class="row">
<highchart id="chart1" config="chartConfig" class="span10"></highchart>
</div>
</div>
</div>

<h3><a href="https://github.com/pablojim/highcharts-ng">See Highcharts-ng on github</a></h3>

0 comments on commit a517b2a

Please sign in to comment.