Having difficulty deciphering the legend in the Highcharts library for Angular (angular-highcharts)

I have a requirement to display two datasets as dual 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 setup is executed in the ngAfterContentInit method

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 data for the columns is populated 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;
  }

This section handles the configuration of the chart

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;
  }
}

The end result should be a chart like https://i.stack.imgur.com/DUFyL.png

There seems to be an issue with duplicate legends appearing on my chart. The columns are correctly structured, but I am unsure why the legend duplicates. Any insights into what might be causing this issue?

Answer №1

Here is the current approach:

private generateColumnSeries(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;
  }

However, having status names hardcoded is not ideal, as they may change. Here is a solution for dynamic keys:

private generateColumnSeries(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;
  }

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Encountering issues with accessing the clientWidth and clientHeight references of the DOM in Vue

Issue with 'clientWidth' and 'clientHeight' properties on Vue and Element types. <div class="invoice-step-detail" id="invoice" ref="invoice"> @Component({ name: 'CreateInvoice', co ...

The 'import type' declaration cannot be parsed by the Babel parser

Whenever I attempt to utilize parser.parse("import type {Element} from 'react-devtools-shared/src/frontend/types';", {sourceType: "unambiguous"}); for parsing the statement, I come across an error stating Unexpected token, exp ...

Enhancing the aesthetic appeal of Angular2 Google Maps

Hey there, I'm a newcomer to Angular2/Typescript and I've been working on styling a map in my Angular2 project. I integrated a map using the Angular2 Google Maps Components, but I'm having trouble with applying styles through the undocumente ...

Issues with sending emails through Nodemailer in a Next.js project using Typescript

I'm currently working on a personal project using Nodemailer along with Next.js and Typescript. This is my first time incorporating Nodemailer into my project, and I've encountered some issues while trying to make it work. I've been followin ...

Discovering the versatility of Typescript objects

I want to define a type that follows this rule: If the property container is present, then expect the property a. If the property item is present, then expect the property b. Both container and item cannot exist at the same time. The code I would expect ...

Removing text that was added via the chart renderer in Highcharts can be accomplished by identifying the specific element

Instead of using a legend in my graph, I have added labels directly to the series (view example here). After drawing the graph with these labels attached, the user can click a button to add another series which hides some existing lines successfully. Howev ...

Angular 16 routing not loading content

I configured the routes in Angular v16 and encountered a blank page issue with the login and register functionality. I suspect it has to do with routing, but after checking multiple times, I couldn't pinpoint the exact problem. Below are snippets of t ...

One way to update the value of the current array or object using ngModel in Angular 2 is to directly

I have a situation where I am dealing with both an array and an object. The array is populated with data retrieved from a service, while the object contains the first element of that array. feesEntries: Array<any> = []; selectedFeesEntry: any; clien ...

What is the best way to determine in component.html whether the column type is equal to 1 to show the label text "Active,"

Having trouble checking the value of an object named ReportControl. If the column type is 1, display the label "active"; otherwise, display the label "not active" on reportcomponent.html. The data for the ReportControl object is as follows: {"reportId": ...

NG0900: Issue encountered while attempting to compare '[object Object]'. Please note that only arrays and iterable objects are permitted for comparison

Experimenting with an Angular project where I am retrieving data from a Minecraft API and displaying it on my website. This is my first time working with Angular's HTTP requests. Encountered the following error code; NG0900: Error trying to diff &apo ...

Exploring the contrast between string enums and string literal types in TypeScript

If I want to restrict the content of myKey in an object like { myKey: '' } to only include either foo, bar, or baz, there are two possible approaches. // Using a String Literal Type type MyKeyType = 'foo' | 'bar' | &ap ...

determining the properties of a given data type

I am currently working with a type that I have obtained from a third party source. My goal is to determine the type of a specific property within that type, using TypeScript. For example: type GivenType = { prop: string; } type desiredType = <&l ...

Using Angular 2, you can pass an object as a parameter to a function

Is there a way to pass an object as a parameter in the DOM on this forum? Within my HTML code, I have the following: <div class="list-items"> <ul> <li *ngFor="let i of item"> <span (click)="onAdd({{newUser.us ...

The issue in Angular2 viewmodel where the boolean value fails to update the *ngIf template

I'm seeking assistance with an unusual issue in my Angular2 and Typescript application. Within my template, I have the following HTML utilizing ngIf: <div *ngIf="loading" class="row"> <div class="small-3 small-centered columns" > ...

Provide the succeeding line from a CSV document whenever a PHP script is executed

I have implemented highcharts to generate a real-time chart with data that updates dynamically. The chart makes an AJAX call to a PHP file which is responsible for parsing a CSV and returning the output as JSON. Below is my Highcharts/jQuery code: functi ...

Book of Tales displaying Emotion Styling upon initial load only

I'm currently working with the styled api provided by emotion in order to create a custom styled button. const StyledButton = styled(Button)` background-color: ${theme.palette.grey['800']}; width: 50; height: 50; &:hover { ba ...

Using the currency pipe with a dynamic variable in Angular 2

My application utilizes CurrencyPipe, The current implementation is functional, <div class="price">{{123 | currConvert | currency:'USD':true:'3.2-2'}}</div> Now, I need to dynamically pass the currency from a model varia ...

Create an interactive Angular form that dynamically generates groups of form elements based on data pulled from

I am currently developing an Angular application and working on creating a dynamic form using Angular. In this project, I am attempting to divide the form into two sections: Person Name and Personal Details. While I have successfully grouped fields for P ...

Angular 13: Issue with Http Interceptor Not Completing Request

In my app, I have implemented a HtppInterceptor to manage a progress bar that starts and stops for most Http requests. However, I encountered an issue with certain api calls where the HttpHandler never finalizes, causing the progress bar to keep running in ...

The TypeScript factory design pattern is throwing an error stating that the property does not

While working with TypeScript, I encountered an issue when trying to implement the factory pattern. Specifically, I am unable to access child functions that do not exist in the super class without encountering a compiler error. Here is the structure of my ...