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

Defining types for functions that retrieve values with a specified default

My method aims to fetch a value asynchronously and return it, providing a default value if the value does not exist. async get(key: string, def_value?: any): Promise<any> { const v = await redisInstance.get(key); return v ? v : def_value; } W ...

React Scheduler by Bryntum

After successfully discovering some functions related to various actions, I find myself still in need of additional functions: Currently, I am utilizing these functions by passing them directly as props to the Scheduler React Component: - onBeforeEventSa ...

Tips for parsing through extensive JSON documents containing diverse data types

In the process of developing an npm package that reads json files and validates their content against predefined json-schemas, I encountered issues when handling larger file sizes (50MB+). When attempting to parse these large files, I faced memory allocati ...

Fixing Email Validation Error in AngularLearn how to troubleshoot and resolve

I'm encountering an issue when trying to develop an email center using regex pattern, as I'm receiving a validator error in HTML. I have already installed ngx-chips and angular-editor, imported all the necessary modules and dependencies. Here is ...

Create a function that will always output an array with the same number of elements as the input

Is there a method to generate a function that specifies "I accept an array of any type, and will return the same type with the same length"? interface FixedLengthArray<T, L extends number> extends Array<T> { length: L; } export function shuf ...

Steps for utilizing a Get() method to view a response within Angular

I am having trouble with implementing an API call on a page and I'm unsure about what's wrong with the Subscribe/Observe method. Currently, this is the code that I have: import { Component, OnInit } from '@angular/core'; import { Ro ...

Ways to turn off a TypeScript error across the entire project

Due to an unresolved TypeScript bug causing a false positive, I am looking to disable a specific TypeScript error for my entire project. How can this be achieved? The requirements are: Disabling only one type of error Not limited to a single line or file ...

Focusing on the active element in Typescript

I am working on a section marked with the class 'concert-landing-synopsis' and I need to add a class to a different element when this section comes into focus during scrolling. Despite exploring various solutions, the focused variable always seem ...

How come this mocha test is exceeding its timeout limit when making a basic call with mongoose?

Trying to write a simple assertion for an asynchronous database method: describe('User Repository', () => { describe('findById', () => { it('Returns a user that can be found in the database by ID', async () => { ...

Attempting to access a specific JSON key using Observables

Apologies for the question, but I'm relatively new to Typescript and Ionic, and I find myself a bit lost on how to proceed. I have a JSON file containing 150 entries that follow a quite simple interface declaration: export interface ReverseWords { id ...

Clerk Bug: The UserResource type returned by useUser() does not match the @clerk/types

When attempting to pass the user obtained from useUser(), an error occurred: The 'UserResource' type is lacking the required properties 'passkeys' and 'createPasskey' from the 'UserResource' type Upon investigating ...

Having trouble retrieving information from a JSON object? Unable to retrieve property 'company_about' of an undefined object?

Here is the JSON data I have: [ { "id": 1, "job_id": 1, "company_profile": "Sales and Marketing", "company_about": "Established in 1992 , it is a renouned marketing company", "company_product": "Ford,Mustang,Beetle", "key_skills": ...

Typescript allows you to apply a filter to an array

Query: Is there a way to display a pre-selected item from my dropdown using its unique ID? Issue/Explanation: The dropdown options in my web service are dynamically fetched based on a user's ZipCode. For example, a Provider is displayed as {Pho ...

Typescript: Maximizing efficiency and accuracy

When it comes to developing Angular2 apps using Typescript, what are the essential best practices that we should adhere to? ...

Enhanced VS code typings for Nuxt injected properties

My approach to injecting properties into the Nuxt TS context is as follows: ~/plugins/services.ts import Vue from 'vue'; import { errorService } from '~/services/error'; import { Plugin } from '@nuxt/types' const services: Pl ...

Dealing with errors in Next.js 13 with middleware: a comprehensive guide

My attempt to manage exceptions in Next.js 13 using middleware is not producing the desired results. Below is my current code: import { NextRequest, NextFetchEvent, NextResponse } from "next/server" export function middleware(req: NextRequest, e ...

The module 'json-stringify-safe' could not be located

Encountering an issue while executing the command - ionic serve The code was functioning properly on a different system but seems to be causing trouble for me at the moment. ...

Developing an npm package for storing a communal instance

I am interested in developing an npm library that can be initialized with a specific set of keys and then utilized throughout an entire project. Here is an illustration of how I envision its usage: In the main component or page import customLib from &quo ...

Angular 14 presents an issue where the injectable 'PlatformLocation' requires compilation with the JIT compiler; however, the '@angular/compiler' module is currently missing

I've encountered the following error and have tried multiple solutions, but none of them have been successful: Error: The injectable 'PlatformLocation' requires JIT compilation with '@angular/compiler', which is not available. ...

Unlocking the value of the "input" field within an EventListener function in Angular directly from the DOM

In my "characters" module, there is a form with a text field and a button. When the button is clicked, it triggers a function but I am struggling to retrieve the current input text and pass it to the async function. HTML: TS: Unfortunately, simply getti ...