Angular Highcharts - How to clone chart dynamically

1.5k Views Asked by At

I am locally using following highcharts dependencies:

  • "angular-highcharts": "latest"
  • "highcharts": "latest"
  • "@types/highcharts": "latest"

Here is a live demo of my source code,

I am extensively using angular-highcharts in my angular 5 application. Alot of time there is a need to expand the chart (when there are alot of data point visible on the chart), to accommodate such cases I though of creating a generic component.

This component named chart-widget displays the chart in a bootstrap card with an option to expand chart, on expanded the same chart is opened in a modal. This generic component would take care of all the logic required to open any chart in a modal, (which would be draggable and resizeable). This way we don't need to replicate same functionality every where.

I made a generic component and every thing used to work fine, but recently we upgraded our repo dependencies as there was some other issues in the highcharts version we were using, those issues were fixed in the latest version of highCharts so we thought its best to upgrade to the latest version. Since then this functionality stopped working.

Following logic used to work for cloning chartConfig at the time of modal open. Then the cloned config was passed to the expanded chart which resides inside the modal. But now the expanded chart is always blank now.

this.expandChartConfig = new Chart(Object.assign({}, this.chartConfig.options));

where chartConfig is the normal config being used for rendering chart,

and expandChartConfig is the chart object passed to the modal.

After upgrading I realized that chartConfig.options property has been made private now, so I also tried:

this.expandChartConfig = new Chart(Object.assign({}, this.chartConfig.ref.options));

but this also didn't work.

Initially I was using the same config for both the charts but that lead to issues as when modal was closed highChart was also destroyed. So I figured instantiating a separate config at the time of opening modal for the chart inside the modal was the best case.

So now In plain words my question is how can I clone an existing chart, dynamically.

  • This functionality is required at dozens of places so I can't maintain separate chart objects at every single place.

  • Also There are alot of operation being performed on the charts, like setData, setCategories, addSeries, removeSeries, update e.t.c. that's why its not recommended to maintaining copies and updating them at every operation. Also these operation would be performed by the parent component so ChartWidgetComponent can't be aware of such changes when they are being performed.

So in short how can I clone an existing highchart dynamically and also whats the best method?

P.s. I tried a bunch of methods mentioned on stackOverflow but none of them seems to be working.

2

There are 2 best solutions below

6
On BEST ANSWER

To achieve the the expected effect, unfortunately it not enough to copy chart.options and pass it to the new one, if you have not defined the series data before (initially). In this case you need to get your data (from response) and assign it to the new component variable, then pass it to the widget and update your series. Here is instructions how to do it:

Add new field in component:

export class AppComponent {
  chartConfig: Chart;
  chartData: Object;
...

Assign it the reponse to the created field:

private setChartData(chartData) {
  const options = this.chartConfig.ref.options;
  if (chartData.categories.length === 0) {
    options.lang.noData = `no data from: ${chartData.customMsgFromDate} to ${chartData.customMsgEndDate}.`;
  } else {
    options.lang.noData = 'No data to display';
  }
this.chartData = chartData;

Pass the data to the widget:

<app-chart-widget [chartConfig]="chartConfig" chartLabel="Title" [chartData]="chartData"></app-chart-widget>

Add the data of every series to the new chart options:

onExpandChart(content): void {
  this.expandChartConfig = new Chart(this.chartConfig.ref.options);
  // Clone series data
  this.expandChartConfig.options.series.forEach((s, i) => {
    let name = s.name.toLowerCase()
    s.data = this.chartData[name]
  })

  this.modalService.open(content, { size: 'xl' as 'lg' });
  setTimeout(() => {
    this.resizeChart();
  ...

Live example: https://stackblitz.com/edit/highcharts-cloning-chart-bo3tfp

Kind regards!

1
On

I'm using Highcharts too, I have defined a chart as a reusable Component when ever I want to paint an other chart I just pass the values througth Input() decorattor

in this case you can use something like this:

Chart Component ts

@Component ({
    selector: 'char-component'
    ...
})
export class CharComponent {
  Input() options: any; 
} 

Reusable Component implementation

<char-component [options]="firstObject"></char-component>
<char-component [options]="secondObject"></char-component>

Component to Reusecode

export clas Sample implements OnInit {
  ngOninit(){
     firstObject = {...} //Already defined
     secondObject = Object.assign(this.firstObject); //create a copy of this object

  }
}

note: if you don't know how many chart are in total you can use an array with the options objects and painted in the template if you need another one just push it into the array

<char-component *ngfor="option of options" [options]="option "></char-component>