Creating a visually appealing stacked bar chart using Chart.js in Angular 9 with multiple bars

Upon integrating Chart.js into my Angular 9 application, I encountered an issue where the expected chart values were not being displayed.

In order to address this problem, I need to structure the barChartData according to the format showcased in this stackblitz Demo.

The API response data is as follows:

let data = [
  { operatorName: 'MBC', label: 'Application', subLabel: 'Positive', count: 1 },
  { operatorName: 'MBC', label: 'Channels', subLabel: 'Negative', count: -1 },
  { operatorName: 'MBC', label: 'Customer Care', subLabel: 'Negative', count: -1 },
  { operatorName: 'MBC', label: 'Customer Care', subLabel: 'Positive', count: 1 },
  { operatorName: 'OSEN+', label: 'Application', subLabel: 'Negative', count: -1 },
  { operatorName: 'OSEN+', label: 'Application', subLabel: 'Positive', count: 1 },
  { operatorName: 'OSEN+', label: 'Channels', subLabel: 'Positive', count: 1},
  { operatorName: 'OSEN+', label: 'Customer Care', subLabel: 'Positive', count: 1 }
];

To populate the barChartData with the API response, here is the anticipated dataset structure:

this.barChartLabels = ['Application', 'Customer Care', 'Package'];
this.barChartData = [
  {
    label: 'OSEN+ Passtive',
    type: 'bar',
    stack: 'OSEN+',
    data: [30, 31, 23],
  },
  {
    label: 'OSEN+ Negative',
    type: 'bar',
    stack: 'OSEN+',
    data: [-15, -16],
  },
  {
    label: 'MBC Passtive',
    type: 'bar',
    stack: 'MBC',
    data: [20, 21],
  },
  {
    label: 'MBC Negative',
    type: 'bar',
    stack: 'MBC',
    data: [-10, -11],
  },
];

I have attempted to implement the logic by creating labels and datasets based on the API response:

let labels = [...new Set(data.map((x) => x.label))];
let operators = [...new Set(data.map((x) => x.operatorName))];
let subLabels = [...new Set(data.map((x) => x.subLabel))];
let subLabelDatasets = subLabels.map((x) => {
  let datasets = [];
  let operatorLabels = [];
  for (let label of labels) {
    datasets.push(
      data.find((y) => y.label == label && y.subLabel == x)?.count || 0
    );
  }

  return {
    label: operatorLabels,
    data: datasets,
  };
});

Desired outcome: https://i.sstatic.net/uJ8jz.png

Answer №1

Comparing my previous response, the underlying concept remains parallel.

Regarding the subLabels, when grouping data by both operatorName and subLabel, it's crucial to ensure distinct groups for objects containing these specific properties.

Within the dataset of each object in subLabelDatasets, you can determine the value by referencing the value based on the parameters of label, operatorName, and subLabel from the main data array.

let labels = [...new Set(data.map((x) => x.label))];
let subLabels = data.reduce((acc, cur: any) => {
  if (
    acc.findIndex(
      (x) =>
        x.operatorName == cur.operatorName && x.subLabel == cur.subLabel
    ) == -1
  )
    acc.push({ operatorName: cur.operatorName, subLabel: cur.subLabel });

  return acc;
}, [] as { operatorName: string; subLabel: string }[]);

let subLabelDatasets = subLabels.map((x) => {
  let datasets = [];

  for (let label of labels) {
    datasets.push(
      data.find(
        (y) =>
          y.label == label &&
          y.subLabel == x.subLabel &&
          y.operatorName == x.operatorName
      )?.count || 0
    );
  }

  return {
    label: x.operatorName + ' ' + x.subLabel,
    data: datasets,
    stack: x.operatorName,
    type: 'bar',
  };
});

this.barChartLabels = labels;
this.barChartData = subLabelDatasets;

Live Demo @ StackBlitz

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 unusual results while utilizing interfaces with overloaded arguments

I came across a situation where TypeScript allows calling a method with the wrong type of argument. Why doesn't the TypeScript compiler flag this as an issue? interface IValue { add(value: IValue): IValue; } class NumberValue implements IValue { ...

Include a Polyfill in craco, implementing a fallback for 'resolve.fallback'

I'm encountering an issue: If you need to use a polyfill, follow these steps: - include a fallback 'resolve.fallback: { "path": require.resolve("path-browserify") }' - install 'path-browserify' If you prefer n ...

Exploring the zorro components (nz-tree) in Angular for effective testing with Jasmine and Karma

How can I access the data in the $event and verify if the treeClick method is being called upon click? When running the test file, I encountered the following error: "Expected spy treeClick to have been called once. It was called 0 times." In t ...

Sending the HTML input value to a Knockout view

Can someone assist me with a dilemma I'm facing? Within CRM on Demand, I have a view that needs to extract values from CRM input fields to conduct a search against CRM via web service. If duplicate records are found, the view should display them. Be ...

I'm looking for the best place to place code for initialization before initializing a custom module in Angular 14

Using ngrx/data 14 and Angular 14, I have constructed a unique custom module that I include in my app.module.ts file as follows: @NgModule({ declarations: [ AppComponent ], imports: [ ... AppRoutingModule, MyCustomModule, ... ] ...

A guide on setting a constant object for template usage

As I delve into constructing elasticsearch queries, I find myself eager to implement object templates to simplify the creation of POST bodies for my queries before they are sent to the data service. Although the initial query construction goes smoothly, I ...

Tag of Component placed after <router-outlet>

partial1.html <div class="content">test test</div> main.html <div class="main"> <router-outlet></router-outlet> </div> Suppose we need to route from main.html to partial1.html. During runtime, the resulting HTML w ...

Tips for setting up a typeorm entity with attention to its nullable fields

How can I assign values to typeorm entities and insert them into the database? import { PricingPatternElement } from file const Element:PricingPatternElement = { displayOrder: 10, elementName: "test", createdAt : getCurrentDate(), createdBy: &qu ...

The struggle of implementing useReducer and Context in TypeScript: A type error saga

Currently attempting to implement Auth using useReducer and Context in a React application, but encountering a type error with the following code snippet: <UserDispatchContext.Provider value={dispatch}> The error message reads as follows: Type &apos ...

Automatically insert a hyphen (-) between each set of 10 digits in a phone number as it is being typed into the text

I'm attempting to automatically insert a hyphen (-) in between 10 digits of a phone number as it is being typed into the text field, following North American standard formatting. I want the output to look like this: 647-364-3975 I am using the keyup ...

Displaying a random element from the state array in React Native

I'm attempting to display a random item from the state array, with the possibility of it changing each time the page reloads. Here's what I have tried so far, any suggestions or ideas are welcome! This is my current state: state = { randomIt ...

Display a React component according to the user's input

Within the first (parent) div, there is an underlined message stating: "This JSX tag's 'children' prop expects a single child of type 'ReactNode', but multiple children were provided.ts(2746)". import A from './components/A&ap ...

Accordion symbol for adding or subtracting

Looking for a way to change the Toggle text in my angular 7 application accordion to images or content displaying a + sign for collapse and - for expand. I need to achieve this using CSS in my SCSS stylesheet so that I can later change the color of the sig ...

Troubleshooting the issue with generateStaticParams() in NextJs/TypeScript

My NextJs app has a products page that should render dynamic routes statically using generateStaticParams(). However, this functionality does not work as expected. When I run "npm run build," it only generates 3 static pages instead of the expected number. ...

Discover the steps to activate and utilize mat-error without the need for form control manipulation

Have you encountered an issue with using ngModel and mat-error without a form? Is there a workaround that allows the use of mat-error with ngModel? #code <mat-form-field appearance="fill" class="w-48per"> <mat-label>Fi ...

Unable to locate the type definition file for 'zen-observable'

While working with Ionic 3, I decided to use the AWS default template and encountered an error: > ionic start app0 aws // aws is a default starting app in Ionic3 > cd app0 > ionic serve However, when I tried to serve the application, I received ...

Attempting to build a table within a table structure

My goal is to create a nested table structure similar to this image: https://i.sstatic.net/v6lZo.png The number of months, topics, and arguments for each topic can vary as they are retrieved from a database. I have attempted to implement this functionali ...

Upgrading from Angular 5 to Angular 7: A seamless migration journey

Following my migration from Angular 5 to Angular 7, I encountered an issue with RxJs operations such as observables and @ngrx/store. Here is the error message I received: ERROR in node_modules/@ngrx/store/src/actions_subject.d.ts(2,10): error TS2305: Mo ...

Building a different theme using the ng or npm command can be achieved by following these steps

In an attempt to utilize different npm commands for building distinct themes, such as running 'npm run red' (to launch index with red.html) or 'npm run blue' (to initiate index with blue.html). npm run red npm run blue I have configur ...

deleting the existing marker before placing a new marker on the Mapbox

Upon the map loading with GeoJson data, I have implemented code to display markers at specified locations. It works flawlessly, but I am seeking a way to remove previous markers when new ones are added. What adjustments should be made for this desired func ...