@@ -15,10 +15,9 @@ import {
15
15
} from '@angular/core' ;
16
16
import { BooleanInput , coerceBooleanProperty , coerceNumberProperty , NumberInput } from '@angular/cdk/coercion' ;
17
17
18
- import { assign , find , merge } from 'lodash' ;
18
+ import { merge } from 'lodash' ;
19
19
20
20
import Chart , { ChartData , ChartOptions , ChartType , Plugin } from 'chart.js/auto' ;
21
- import * as chartjs from 'chart.js' ;
22
21
23
22
import { customTooltips as cuiCustomTooltips } from '@coreui/chartjs' ;
24
23
import { IChartjs } from './chartjs.interface' ;
@@ -52,7 +51,7 @@ export class ChartjsComponent implements IChartjs, AfterViewInit, OnDestroy, OnC
52
51
53
52
@Input ( ) id = `c-chartjs-${ nextId ++ } ` ;
54
53
@Input ( ) options ?: ChartOptions ;
55
- @Input ( ) plugins ? : Plugin [ ] ;
54
+ @Input ( ) plugins : Plugin [ ] = [ ] ;
56
55
57
56
@Input ( )
58
57
set redraw ( value : boolean ) {
@@ -92,13 +91,13 @@ export class ChartjsComponent implements IChartjs, AfterViewInit, OnDestroy, OnC
92
91
} ;
93
92
}
94
93
95
- get computedData ( ) {
94
+ get chartData ( ) {
96
95
const canvasRef = this . canvasElement . nativeElement ;
97
96
return typeof this . data === 'function'
98
97
? canvasRef . value
99
98
? this . data ( canvasRef . value )
100
- : { datasets : [ ] }
101
- : merge ( { } , this . data ) ;
99
+ : { labels : [ ] , datasets : [ ] }
100
+ : merge ( { labels : [ ] , datasets : [ ] } , { ... this . data } ) ;
102
101
}
103
102
104
103
constructor (
@@ -108,21 +107,21 @@ export class ChartjsComponent implements IChartjs, AfterViewInit, OnDestroy, OnC
108
107
) { }
109
108
110
109
ngAfterViewInit ( ) : void {
111
- this . setupChart ( ) ;
112
- this . updateChart ( ) ;
110
+ this . chartRender ( ) ;
111
+ this . chartUpdate ( ) ;
113
112
}
114
113
115
114
ngOnChanges ( changes : SimpleChanges ) : void {
116
115
if ( ! changes . data . firstChange ) {
117
- this . updateChart ( ) ;
116
+ this . chartUpdate ( ) ;
118
117
}
119
118
}
120
119
121
120
ngOnDestroy ( ) : void {
122
- this . destroyChart ( ) ;
121
+ this . chartDestroy ( ) ;
123
122
}
124
123
125
- handleOnClick ( $event : MouseEvent ) {
124
+ public handleOnClick ( $event : MouseEvent ) {
126
125
if ( ! this . chart ) return ;
127
126
128
127
const datasetAtEvent = this . chart . getElementsAtEventForMode ( $event , 'dataset' , { intersect : true } , false ) ;
@@ -135,42 +134,50 @@ export class ChartjsComponent implements IChartjs, AfterViewInit, OnDestroy, OnC
135
134
this . getElementsAtEvent . emit ( elementsAtEvent ) ;
136
135
}
137
136
138
- destroyChart ( ) {
137
+ public chartDestroy ( ) {
139
138
this . chart ?. destroy ( ) ;
140
139
}
141
140
142
- setupChart ( ) {
141
+ public chartRender ( ) {
143
142
if ( ! this . canvasElement ) return ;
144
143
145
144
if ( this . customTooltips ) {
146
- chartjs . defaults . plugins . tooltip . enabled = false ;
147
- chartjs . defaults . plugins . tooltip . mode = 'index' ;
148
- chartjs . defaults . plugins . tooltip . position = 'nearest' ;
149
- chartjs . defaults . plugins . tooltip . external = cuiCustomTooltips ;
145
+ const options = this . options
146
+ this . options = {
147
+ ...options ,
148
+ plugins : {
149
+ ...options ?. plugins ,
150
+ tooltip : {
151
+ ...options ?. plugins ?. tooltip ,
152
+ enabled : false ,
153
+ mode : 'index' ,
154
+ position : 'nearest' ,
155
+ external : cuiCustomTooltips
156
+ }
157
+ }
158
+ } ;
150
159
}
151
160
152
161
const ctx : CanvasRenderingContext2D = this . canvasElement . nativeElement . getContext ( '2d' ) ;
153
162
154
163
return this . ngZone . runOutsideAngular ( ( ) => {
155
- this . chart = new Chart ( ctx , {
156
- type : this . type ,
157
- data : this . computedData ,
158
- options : this . options as ChartOptions ,
159
- plugins : this . plugins
160
- } ) ;
161
- setTimeout ( ( ) => {
162
- this . renderer . setStyle ( this . canvasElement . nativeElement , 'display' , 'block' ) ;
163
- } )
164
+ const config = this . chartConfig ( ) ;
165
+ if ( config ) {
166
+ this . chart = new Chart ( ctx , config ) ;
167
+ setTimeout ( ( ) => {
168
+ this . renderer . setStyle ( this . canvasElement . nativeElement , 'display' , 'block' ) ;
169
+ } ) ;
170
+ }
164
171
} ) ;
165
172
}
166
173
167
- updateChart ( ) {
174
+ chartUpdate ( ) {
168
175
if ( ! this . chart ) return ;
169
176
170
177
if ( this . redraw ) {
171
- this . destroyChart ( ) ;
178
+ this . chartDestroy ( ) ;
172
179
setTimeout ( ( ) => {
173
- this . setupChart ( ) ;
180
+ this . chartRender ( ) ;
174
181
} ) ;
175
182
return ;
176
183
}
@@ -179,49 +186,47 @@ export class ChartjsComponent implements IChartjs, AfterViewInit, OnDestroy, OnC
179
186
this . chart . options = { ...this . options } ;
180
187
}
181
188
189
+ const config = this . chartConfig ( ) ;
190
+
182
191
if ( ! this . chart . config . data ) {
183
- this . chart . config . data = this . computedData ;
184
- this . ngZone . runOutsideAngular ( ( ) => {
185
- this . chart ?. update ( ) ;
186
- } ) ;
187
- return ;
192
+ this . chart . config . data = { ...config . data } ;
193
+ this . chartUpdateOutsideAngular ( ) ;
188
194
}
189
195
190
- const { datasets : newDataSets = [ ] , ...newChartData } = this . computedData ;
191
- const { datasets : currentDataSets = [ ] } = this . chart . config . data ;
192
-
193
- // copy values
194
- assign ( this . chart . config . data , newChartData ) ;
195
- this . chart . config . data . datasets = newDataSets . map ( ( newDataSet : any ) => {
196
- // given the new set, find it's current match
197
- const currentDataSet = find (
198
- currentDataSets ,
199
- ( d : any ) => d . label === newDataSet . label && d . type === newDataSet . type
200
- ) ;
201
-
202
- // There is no original to update, so simply add new one
203
- if ( ! currentDataSet || ! newDataSet . data ) return newDataSet ;
204
-
205
- if ( ! currentDataSet . data ) {
206
- currentDataSet . data = [ ] ;
207
- } else {
208
- currentDataSet . data . length = newDataSet . data . length ;
209
- }
196
+ if ( this . chart ) {
197
+ Object . assign ( this . chart . config . options , config . options ) ;
198
+ Object . assign ( this . chart . config . plugins , config . plugins ) ;
199
+ Object . assign ( this . chart . config . data , config . data ) ;
200
+ }
210
201
211
- // copy in values
212
- assign ( currentDataSet . data , newDataSet . data ) ;
202
+ this . chartUpdateOutsideAngular ( ) ;
203
+ }
213
204
214
- // apply dataset changes, but keep copied data
215
- return {
216
- ...currentDataSet ,
217
- ...newDataSet ,
218
- data : currentDataSet . data
219
- } ;
220
- } ) ;
205
+ private chartUpdateOutsideAngular ( ) {
221
206
setTimeout ( ( ) => {
222
207
this . ngZone . runOutsideAngular ( ( ) => {
223
208
this . chart ?. update ( ) ;
224
209
} ) ;
225
- } )
210
+ } ) ;
211
+ }
212
+
213
+ public chartToBase64Image ( ) : string | undefined {
214
+ return this . chart ?. toBase64Image ( ) ;
215
+ }
216
+
217
+ private chartDataConfig ( ) {
218
+ return {
219
+ labels : this . chartData ?. labels ?? [ ] ,
220
+ datasets : [ ...this . chartData ?. datasets ] ?? [ ]
221
+ } ;
222
+ }
223
+
224
+ private chartConfig ( ) {
225
+ return {
226
+ data : this . chartDataConfig ( ) ,
227
+ options : this . options as ChartOptions ,
228
+ plugins : this . plugins ,
229
+ type : this . type
230
+ } ;
226
231
}
227
232
}
0 commit comments