Search code examples
typescripthighchartsangular-highcharts

I'm getting a confusing legend in Highcharts (angular-highcharts)


I have two data sets that I want to format into two column charts.

(2) [{…}, {…}]
    0:
    historyDate: "2021-02-10T10:00:000Z"
    documentStatusHistory:
        CANCELLED: 6
        COMPLETED: 52
        IN_PROGRESS: 1
        OPEN: 1
        PHASE_OUT: 1
    1:
    historyDate: "2021-02-17T10:00:000Z"
    documentStatusHistory:
        CANCELLED: 6
        COMPLETED: 52
        IN_PROGRESS: 1
        OPEN: 1
        PHASE_OUT: 1

The chart is configured in ngAfterContentInit

ngAfterContentInit() {
    const chartOptions: Options = this.createDefaultColumnOptions();
    chartOptions.title.text = this.chartTitle;
    this.resultChart = Highcharts.chart(this.resultChartTarget.nativeElement, chartOptions);
    this.resultChart.addSeries(this.buildColumnSeries(this.uuidService.getNewUuid(), this.chartData));
  }

The column data are created here

private buildColumnSeries(id: string, chartData: any[]) {
    const options: SeriesOptions = {
      id,
      type: 'column',
      data: [],
    } as SeriesOptions;

    chartData.forEach((item) => {
      const date = new Date(item.historyDate);
      const newDate = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate());

      for (const status in item.documentStatusHistory) {
        this.resultChart.addSeries({
                                     type: 'column',
                                     yAxis: 0,
                                     name: status.replace('_', ' '),
                                     data: [[newDate, item.documentStatusHistory[status]]],
                                     color: DisplayUtil.getStatusColor(status)
                                   });
      }
    });
    return options;
  }

and here the chart options

private createDefaultColumnOptions(): Options {
    return {
      chart: {
        zoomType: 'xy',
        marginLeft: 70,
        marginRight: 70
      },
      title: {
        useHTML: true
      },
      legend: {
        verticalAlign: 'top',
        labelFormat: '<b>{name}</b>',
        enabled: true
      },
      xAxis: {
        type: 'datetime',
        dateTimeLabelFormats: {
          week: '%e. %b'
        },
        title: {
          text: 'Date'
        }
      },
      yAxis: [
        { // Primary yAxis
          title: {
            text: 'Total ' + this.chartTitle
          }
        }
      ],
      tooltip: {
        shared: true
      },
      plotOptions: {
        column: {
          stacking: 'normal'
        },
        series: {
          cursor: 'pointer'
        }
      },
      credits: {
        enabled: false
      }
    } as Options;
  }
}

At the end I get the following chart

enter image description here

I am now not entirely clear why I am getting the legend twice. the columns are each correctly structured. Does anyone have any indication of what's wrong here?


Solution

  • The current solution is as follows.

    private buildColumnSeries(id: string, chartData: any[]) {
    
        const options: SeriesOptions = {
          id,
          type: 'column',
          data: [],
        } as SeriesOptions;
    
        console.log(chartData);
    
        const OPEN = [];
        const COMPLETED = [];
        const IN_PROGRESS = [];
        const PHASE_OUT = [];
        const CANCELLED = [];
    
        chartData.forEach((item) => {    
          OPEN.push(item.documentStatusHistory.OPEN);
          COMPLETED.push(item.documentStatusHistory.COMPLETED);
          IN_PROGRESS.push(item.documentStatusHistory.IN_PROGRESS);
          PHASE_OUT.push(item.documentStatusHistory.PHASE_OUT);
          CANCELLED.push(item.documentStatusHistory.CANCELLED);
        });
    
        this.resultChart.addSeries({
                                     type: 'column',
                                     yAxis: 0,
                                     name: 'OPEN',
                                     data: OPEN,
                                     color: DisplayUtil.getStatusColor('OPEN')
                                   });
    
        this.resultChart.addSeries({
                                     type: 'column',
                                     yAxis: 0,
                                     name: 'COMPLETED',
                                     data: COMPLETED,
                                     color: DisplayUtil.getStatusColor('COMPLETED')
                                   });
    
        this.resultChart.addSeries({
                                     type: 'column',
                                     yAxis: 0,
                                     name: 'IN_PROGRESS',
                                     data: IN_PROGRESS,
                                     color: DisplayUtil.getStatusColor('IN_PROGRESS')
                                   });
    
        this.resultChart.addSeries({
                                     type: 'column',
                                     yAxis: 0,
                                     name: 'PHASE_OUT',
                                     data: PHASE_OUT,
                                     color: DisplayUtil.getStatusColor('PHASE_OUT')
                                   });
    
        this.resultChart.addSeries({
                                     type: 'column',
                                     yAxis: 0,
                                     name: 'CANCELLED',
                                     data: CANCELLED,
                                     color: DisplayUtil.getStatusColor('CANCELLED')
                                   });
    
        return options;
      }
    

    But I don't want to keep the status hard-coded but dynamic, because I don't know whether the status names can change.

    so after a while, here the solution for dynamic kays.

    private buildColumnSeries(id: string, chartData: any[]) {
    
        const options: SeriesOptions = {
          id,
          type: 'column',
          data: [],
        } as SeriesOptions;
    
        const data = [];
    
        const map = new Map();
    
        chartData.forEach((item) => {
    
          const keys = Object.keys(item.documentStatusHistory);
          keys.forEach((key) => {
    
            let found = false;
            data.find(element => {
              if (element.key === key) {
                found = true;
              }
            });
    
            if (found) {
              const array = map.get(key);
              array.push(item.documentStatusHistory[key]);
              map.set(key, array);
            } else {
              data.push({key, series: [1]});
              map.set(key, [item.documentStatusHistory[key]]);
            }
    
          });
        });
    
        map.forEach((value: [], key: string) => {
          this.resultChart.addSeries({
                                       type: 'column',
                                       yAxis: 0,
                                       name: key,
                                       data: value,
                                       color: DisplayUtil.getStatusColor(key)
                                     });
        });
        return options;
      }