@@ -15,10 +15,9 @@ import {
1515} from '@angular/core' ;
1616import { BooleanInput , coerceBooleanProperty , coerceNumberProperty , NumberInput } from '@angular/cdk/coercion' ;
1717
18- import { assign , find , merge } from 'lodash' ;
18+ import { merge } from 'lodash' ;
1919
2020import Chart , { ChartData , ChartOptions , ChartType , Plugin } from 'chart.js/auto' ;
21- import * as chartjs from 'chart.js' ;
2221
2322import { customTooltips as cuiCustomTooltips } from '@coreui/chartjs' ;
2423import { IChartjs } from './chartjs.interface' ;
@@ -52,7 +51,7 @@ export class ChartjsComponent implements IChartjs, AfterViewInit, OnDestroy, OnC
5251
5352 @Input ( ) id = `c-chartjs-${ nextId ++ } ` ;
5453 @Input ( ) options ?: ChartOptions ;
55- @Input ( ) plugins ? : Plugin [ ] ;
54+ @Input ( ) plugins : Plugin [ ] = [ ] ;
5655
5756 @Input ( )
5857 set redraw ( value : boolean ) {
@@ -92,13 +91,13 @@ export class ChartjsComponent implements IChartjs, AfterViewInit, OnDestroy, OnC
9291 } ;
9392 }
9493
95- get computedData ( ) {
94+ get chartData ( ) {
9695 const canvasRef = this . canvasElement . nativeElement ;
9796 return typeof this . data === 'function'
9897 ? canvasRef . value
9998 ? this . data ( canvasRef . value )
100- : { datasets : [ ] }
101- : merge ( { } , this . data ) ;
99+ : { labels : [ ] , datasets : [ ] }
100+ : merge ( { labels : [ ] , datasets : [ ] } , { ... this . data } ) ;
102101 }
103102
104103 constructor (
@@ -108,21 +107,21 @@ export class ChartjsComponent implements IChartjs, AfterViewInit, OnDestroy, OnC
108107 ) { }
109108
110109 ngAfterViewInit ( ) : void {
111- this . setupChart ( ) ;
112- this . updateChart ( ) ;
110+ this . chartRender ( ) ;
111+ this . chartUpdate ( ) ;
113112 }
114113
115114 ngOnChanges ( changes : SimpleChanges ) : void {
116115 if ( ! changes . data . firstChange ) {
117- this . updateChart ( ) ;
116+ this . chartUpdate ( ) ;
118117 }
119118 }
120119
121120 ngOnDestroy ( ) : void {
122- this . destroyChart ( ) ;
121+ this . chartDestroy ( ) ;
123122 }
124123
125- handleOnClick ( $event : MouseEvent ) {
124+ public handleOnClick ( $event : MouseEvent ) {
126125 if ( ! this . chart ) return ;
127126
128127 const datasetAtEvent = this . chart . getElementsAtEventForMode ( $event , 'dataset' , { intersect : true } , false ) ;
@@ -135,42 +134,50 @@ export class ChartjsComponent implements IChartjs, AfterViewInit, OnDestroy, OnC
135134 this . getElementsAtEvent . emit ( elementsAtEvent ) ;
136135 }
137136
138- destroyChart ( ) {
137+ public chartDestroy ( ) {
139138 this . chart ?. destroy ( ) ;
140139 }
141140
142- setupChart ( ) {
141+ public chartRender ( ) {
143142 if ( ! this . canvasElement ) return ;
144143
145144 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+ } ;
150159 }
151160
152161 const ctx : CanvasRenderingContext2D = this . canvasElement . nativeElement . getContext ( '2d' ) ;
153162
154163 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+ }
164171 } ) ;
165172 }
166173
167- updateChart ( ) {
174+ chartUpdate ( ) {
168175 if ( ! this . chart ) return ;
169176
170177 if ( this . redraw ) {
171- this . destroyChart ( ) ;
178+ this . chartDestroy ( ) ;
172179 setTimeout ( ( ) => {
173- this . setupChart ( ) ;
180+ this . chartRender ( ) ;
174181 } ) ;
175182 return ;
176183 }
@@ -179,49 +186,47 @@ export class ChartjsComponent implements IChartjs, AfterViewInit, OnDestroy, OnC
179186 this . chart . options = { ...this . options } ;
180187 }
181188
189+ const config = this . chartConfig ( ) ;
190+
182191 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 ( ) ;
188194 }
189195
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+ }
210201
211- // copy in values
212- assign ( currentDataSet . data , newDataSet . data ) ;
202+ this . chartUpdateOutsideAngular ( ) ;
203+ }
213204
214- // apply dataset changes, but keep copied data
215- return {
216- ...currentDataSet ,
217- ...newDataSet ,
218- data : currentDataSet . data
219- } ;
220- } ) ;
205+ private chartUpdateOutsideAngular ( ) {
221206 setTimeout ( ( ) => {
222207 this . ngZone . runOutsideAngular ( ( ) => {
223208 this . chart ?. update ( ) ;
224209 } ) ;
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+ } ;
226231 }
227232}
0 commit comments