Skip to content

Commit 689fd84

Browse files
jtblinbchekuritrungkien1208
committed
feat: implement plugins support, force update, and dynamic options
- Added chart-plugins support (#643) - Added chart-display-when-no-data and chart-force-update (#724) - Implemented dynamic chart-options updates (#724) Co-authored-by: BHARATH CHEKURI <bchekuri@outlook.com> Co-authored-by: Kien Luu <luutrungkien120894@gmail.com>
1 parent 0083dcb commit 689fd84

2 files changed

Lines changed: 160 additions & 9 deletions

File tree

src/angular-chart.js

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -102,13 +102,16 @@ function ChartJsFactory(ChartJs, $timeout) {
102102
chartClick: '=?',
103103
chartHover: '=?',
104104
chartDatasetOverride: '=?',
105+
chartPlugins: '=?',
106+
chartForceUpdate: '=?',
107+
chartDisplayWhenNoData: '=?',
105108
},
106109
link: function(scope, elem/* , attrs */) {
107110
// Order of setting "watch" matter
108111
scope.$watch('chartData', watchData, true);
109112
scope.$watch('chartSeries', watchOther, true);
110113
scope.$watch('chartLabels', watchOther, true);
111-
scope.$watch('chartOptions', watchOther, true);
114+
scope.$watch('chartOptions', watchOptions, true);
112115
scope.$watch('chartColors', watchOther, true);
113116
scope.$watch('chartDatasetOverride', watchOther, true);
114117
scope.$watch('chartType', watchType, false);
@@ -122,8 +125,7 @@ function ChartJsFactory(ChartJs, $timeout) {
122125
});
123126

124127
function watchData(newVal, oldVal) {
125-
if (! newVal || ! newVal.length ||
126-
(Array.isArray(newVal[0]) && ! newVal[0].length)) {
128+
if (! scope.chartDisplayWhenNoData && isDataEmpty(newVal)) {
127129
destroyChart(scope);
128130
return;
129131
}
@@ -132,13 +134,29 @@ function ChartJsFactory(ChartJs, $timeout) {
132134
return;
133135
}
134136

135-
if (scope.chart && canUpdateChart(newVal, oldVal)) {
137+
if (scope.chart &&
138+
(canUpdateChart(newVal, oldVal) || scope.chartForceUpdate)) {
136139
return updateChart(newVal, scope);
137140
}
138141

139142
createChart(chartType, scope, elem);
140143
}
141144

145+
function isDataEmpty(data) {
146+
return ! data || ! data.length ||
147+
(Array.isArray(data[0]) && ! data[0].length);
148+
}
149+
150+
function watchOptions(newVal, oldVal) {
151+
if (isEmpty(newVal)) {
152+
return;
153+
}
154+
if (angular.equals(newVal, oldVal)) {
155+
return;
156+
}
157+
updateChartOptions(newVal, scope);
158+
}
159+
142160
function watchOther(newVal, oldVal) {
143161
if (isEmpty(newVal)) {
144162
return;
@@ -171,7 +189,8 @@ function ChartJsFactory(ChartJs, $timeout) {
171189

172190
function createChart(type, scope, elem) {
173191
const options = getChartOptions(type, scope);
174-
if (! hasData(scope) || ! canDisplay(type, scope, elem, options)) {
192+
if (! scope.chartDisplayWhenNoData &&
193+
(! hasData(scope) || ! canDisplay(type, scope, elem, options))) {
175194
return;
176195
}
177196

@@ -188,6 +207,7 @@ function ChartJsFactory(ChartJs, $timeout) {
188207
type: type,
189208
data: data,
190209
options: options,
210+
plugins: scope.chartPlugins,
191211
});
192212
scope.$emit('chart-create', scope.chart);
193213
bindEvents(cvs, scope);
@@ -385,6 +405,16 @@ function ChartJsFactory(ChartJs, $timeout) {
385405
return angular.extend({}, ChartJs.getOptions(type), scope.chartOptions);
386406
}
387407

408+
function updateChartOptions(values, scope) {
409+
if (values && scope.chart) {
410+
Object.keys(values).forEach((key) => {
411+
scope.chart.options[key] = values[key];
412+
});
413+
scope.chart.update();
414+
scope.$emit('chart-update', scope.chart);
415+
}
416+
}
417+
388418
function bindEvents(cvs, scope) {
389419
cvs.onclick = scope.chartClick ?
390420
getEventHandler(scope, 'chartClick', false) :

test/test.unit.js

Lines changed: 125 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,7 @@ describe('Unit testing', function() {
405405
.to.equal(true);
406406
});
407407

408-
['labels', 'colors', 'series', 'options'].forEach(function(attr) {
408+
['labels', 'colors', 'series'].forEach(function(attr) {
409409
it('re-creates the chart on ' + attr + ' changes', function() {
410410
const markup = '<div style="width: 250px; height:120px">' +
411411
'<canvas class="chart chart-line" chart-data="data" ' +
@@ -443,16 +443,48 @@ describe('Unit testing', function() {
443443
case 'series':
444444
scope.series = ['Series C', 'Series D'];
445445
break;
446-
case 'options':
447-
scope.options = {scaleShowVerticalLines: true};
448-
break;
449446
}
450447
scope.$digest();
451448

452449
expect(count).to.equal(2);
453450
});
454451
});
455452

453+
it('updates the chart on options changes', function() {
454+
const markup = '<div style="width: 250px; height:120px">' +
455+
'<canvas class="chart chart-line" chart-data="data" ' +
456+
'chart-labels="labels" chart-series="series" ' +
457+
'chart-colors="colors" chart-options="options"></canvas></div>';
458+
let countCreate = 0;
459+
let countUpdate = 0;
460+
461+
scope.options = {scaleShowVerticalLines: false};
462+
scope.labels = [
463+
'January', 'February', 'March', 'April', 'May', 'June', 'July',
464+
];
465+
scope.series = ['Series A', 'Series B'];
466+
scope.colors = ['#45b7cd', '#ff6384'];
467+
scope.data = [
468+
[65, 59, 80, 81, 56, 55, 40],
469+
[28, 48, 40, 19, 86, 27, 90],
470+
];
471+
scope.$on('chart-create', function() {
472+
countCreate++;
473+
});
474+
scope.$on('chart-update', function() {
475+
countUpdate++;
476+
});
477+
478+
$compile(markup)(scope);
479+
scope.$digest();
480+
481+
scope.options = {scaleShowVerticalLines: true};
482+
scope.$digest();
483+
484+
expect(countCreate).to.equal(1);
485+
expect(countUpdate).to.equal(1);
486+
});
487+
456488
['labels', 'colors', 'series', 'options'].forEach(function(attr) {
457489
it('does not re-create the chart on ' + attr +
458490
' not changed', function() {
@@ -500,5 +532,94 @@ describe('Unit testing', function() {
500532
expect(count).to.equal(1);
501533
});
502534
});
535+
536+
describe('PR Features testing', function() {
537+
describe('chart-plugins (#643)', function() {
538+
it('passes plugins to Chart constructor', function() {
539+
const markup = '<canvas class="chart chart-line" chart-data="data" ' +
540+
'chart-labels="labels" chart-plugins="plugins"></canvas>';
541+
scope.labels = ['Monday'];
542+
scope.data = [[1]];
543+
scope.plugins = [{id: 'testPlugin'}];
544+
545+
const spyChart = sandbox.spy(ChartJs, 'Chart');
546+
547+
$compile(markup)(scope);
548+
scope.$digest();
549+
550+
expect(spyChart).to.have.been.calledWithExactly(
551+
sinon.match.any,
552+
sinon.match({
553+
plugins: scope.plugins,
554+
}),
555+
);
556+
});
557+
});
558+
559+
describe('chart-display-when-no-data (#724)', function() {
560+
it('creates chart even if data is empty when ' +
561+
'chart-display-when-no-data is true', function() {
562+
const markup = '<canvas class="chart chart-line" chart-data="data" ' +
563+
'chart-labels="labels" chart-display-when-no-data="true"></canvas>';
564+
scope.labels = [];
565+
scope.data = [];
566+
567+
let created = false;
568+
scope.$on('chart-create', function() {
569+
created = true;
570+
});
571+
572+
$compile(markup)(scope);
573+
scope.$digest();
574+
575+
expect(created).to.be.true;
576+
});
577+
578+
it('destroys chart if data becomes empty and ' +
579+
'chart-display-when-no-data is false (default)', function() {
580+
const markup = '<canvas class="chart chart-line" chart-data="data" ' +
581+
'chart-labels="labels"></canvas>';
582+
scope.labels = ['Monday'];
583+
scope.data = [[1]];
584+
585+
$compile(markup)(scope);
586+
scope.$digest();
587+
588+
let destroyed = false;
589+
scope.$on('chart-destroy', function() {
590+
destroyed = true;
591+
});
592+
593+
scope.data = [];
594+
scope.$digest();
595+
596+
expect(destroyed).to.be.true;
597+
});
598+
});
599+
600+
describe('chart-force-update (#724)', function() {
601+
it('updates chart even if data shape is same when ' +
602+
'chart-force-update is true', function() {
603+
const markup = '<canvas class="chart chart-line" chart-data="data" ' +
604+
'chart-labels="labels" chart-force-update="true"></canvas>';
605+
scope.labels = ['Monday'];
606+
scope.data = [[1]];
607+
608+
$compile(markup)(scope);
609+
scope.$digest();
610+
611+
let updated = false;
612+
scope.$on('chart-update', function() {
613+
updated = true;
614+
});
615+
616+
// Same shape, normally wouldn't update/recreate
617+
scope.data = [[2]];
618+
scope.$digest();
619+
620+
expect(updated).to.be.true;
621+
});
622+
});
623+
});
503624
});
504625
});

0 commit comments

Comments
 (0)