Revamp your visual data displays using Chart.js in conjunction with Angular

I am facing an issue with updating Chart.js in Angular while utilizing ngrx store.

When trying to update the chart data in the selector subscriber (executed in ngOnInit), I encountered the following code:

this.yrSubscription = this.userDataStore.pipe(select(selectYrReport))
  .subscribe(el => {
    el.sessions.forEach(item => {
      this.datasetsSessions[0].data.push(+item);
    });
  });

This is my current chart data:

datasetsSessions: ChartDataSets[] = [{
  label: 'Sessions',
  data: [],
  fill: false
}];

Chart registration:

private _registerCustomChartJSPlugin(): void {
  (window as any).Chart.plugins.register({
    afterDatasetsDraw: (chart, easing): any => {
      if (!chart.options.plugins.xLabelsOnTop
        || (chart.options.plugins.xLabelsOnTop && chart.options.plugins.xLabelsOnTop.active === false)) {
        return;
      }

      const ctx = chart.ctx;

      chart.data.datasets.forEach((dataset, i): any => {
        const meta = chart.getDatasetMeta(i);
        if (!meta.hidden) {
          meta.data.forEach((element, index): any => {
            // Drawing text in white, with specific font details
            ctx.fillStyle = 'rgba(255, 255, 255, 0.7)';
            const fontSize = 13;
            const fontStyle = 'normal';
            const fontFamily = 'Roboto, Helvetica Neue, Arial';
            ctx.font = (window as any).Chart.helpers.fontString(fontSize, fontStyle, fontFamily);

            const dataString = dataset.data[index].toString() + 'k';

            ctx.textAlign = 'center';
            ctx.textBaseline = 'middle';
            const padding = 15;
            const startY = 24;
            const position = element.tooltipPosition();
            ctx.fillText(dataString, position.x, startY);

            ctx.save();

            ctx.beginPath();
            ctx.setLineDash([5, 3]);
            ctx.moveTo(position.x, startY + padding);
            ctx.lineTo(position.x, position.y - padding);
            ctx.strokeStyle = 'rgba(255,255,255,0.12)';
            ctx.stroke();

            ctx.restore();
          });
        }
      });
    }
  });
}

The function is called in the constructor of the component.

Even though I am aware that I need to run chart.update(), I keep encountering errors stating that the chart is undefined.

Answer №1

Don't worry about calling chart.update() when using ng2-charts. Let Angular's change detection handle it for you. If things aren't working as expected, simply reassign the data array after adding a new value to it. You can achieve this by using Array.slice() like shown below.

this.yrSubscription = this.userDataStore.pipe(select(selectYrReport))
  .subscribe(el => {
    el.sessions.forEach(item => {
      this.datasetsSessions[0].data.push(+item);
      this.datasetsSessions[0].data = this.datasetsSessions[0].data.slice();
    });
  }); 

The main issue could be in the _registerCustomChartJSPlugin method. Instead of having a separate method, define its content inside a chartPlugins variable like this:

chartPlugins = [{
  afterDatasetsDraw: (chart, easing): any => {
    ...
  }
}];

In your HTML template, make sure to include the chartPlugins as follows.

<canvas baseChart
  [datasets]="datasetsSessions"
  [plugins]="chartPlugins"
  ...
</canvas>

For further reference, take a look at this StackBlitz.

Answer №2

If you are working with multiple charts within the same component, it is important to maintain a reference to your custom chart. Start by adding a reference to your custom chart instance in the component class like this:

myChart: CustomChart;

Next, in the afterDatasetsDraw method, assign the current chart instance to your custom chart variable:

this.myChart = chart;

Finally, make sure to update the chart in your subscription block:

this.chart.update();

Answer №3

The Component Decorator above contains the following code:

var require: any;
var Chart = require('chart.js');

A property is included in the component to hold an instance of the chart

public chart: Chart;

It's important to note that Chart.plugins.register will register a plugin globally for all charts. https://www.chartjs.org/docs/latest/developers/plugins.html#global-plugins

A chart is required for this plugin to be applied

Assuming you have a Canvas element in your HTML like this:

<div>
  <canvas id="canvas">{{ chart }}</canvas>
</div>

You should update your ngOnInit method to use ngrx Subscription for creating a new chart if it doesn't exist.

ngOnInit() {
this._registerCustomChartJSPlugin();
this.yrSubscription = this.userDataStore.pipe(select(selectYrReport))
  .subscribe(el => {
      if(!this.chart) {
        this.createChart();
      }
    el.sessions.forEach(item => {
      this.datasetsSessions[0].data.push(+item);
    });
    this.chart.update();
  });
}
public createChart() {
   this.chart = new Chart('canvas'); //Include if any object required as second Parameter
}

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

JavaScript Conversion from Binary to Decimal

This code is meant to convert a binary string ("100101") to its decimal equivalent. I am struggling to identify the issue causing it to not work properly. Any assistance would be greatly appreciated. function BinaryConverter(str) { var num=str.split("") ...

Receiving user input in Angular 6 for a function

Need help getting input from a text box and passing it to a function. Encountering the error message "_co.randomCode is not a function." New to Angular and have tried various approaches but can't seem to solve this issue. app.component.html <div ...

Screen JSON data by applying filters

In my current project, I am working on extracting data from a JSON file and presenting it to the user in a way that allows them to input values that match the expected data. My goal is to show different sections of the screen at different times. The initi ...

Discovering the geographical location of all users using Node.js: A step-by-step guide

My current task involves determining the geoip location of users. I have implemented a code that stores the user's address in the database and then displays the geoip location accordingly. However, if a user changes their location and logs in from a d ...

Struggling with transitioning from TypeScript to React when implementing react-data-grid 7.0.0

I'm trying to add drag and drop functionality to my React project using react-data-grid, but I keep encountering a "TypeError: Object(...) is not a function" error. I have a TypeScript version of the file in the sandbox as a reference, but when I try ...

Add aesthetic flair to scrollable containers

I am currently working on displaying data in columns using ngFor. However, I've run into an issue where the styles I apply to the column div are only visible on the top part of the column. As I scroll down to the bottom, the styles disappear. I believ ...

"Troubleshooting: Why is the JavaScript canvas.toDataURL method returning an

While I know this question has been asked multiple times before, I still haven't been able to find a solution. My goal is to take an image, rotate it, and place it in an HTML canvas. To achieve this, I am utilizing another canvas where I rotate the i ...

Reducer is unaware of my current state within react redux

I am currently developing an application that allows users to add items to a collection. The process involves the user inputting the name of the collection item, and the corresponding item is added to the collection. However, I encountered an issue when di ...

Guide on accessing CGI Script response using AJAX

Greetings, Here is the situation: I'm working on a login form. Once the submit button is clicked, it needs to trigger a CGI script called "someurl?userName=user&password=pwd". Depending on the response from the script, I should be able to navigat ...

Transforming API Response into a structured format to showcase a well-organized list

When I make an API call for a list of properties, the data comes back unorganized. Here is how the data from the API looks when stored in vuex: posts:[ { id: 1; title: "Place", acf: { address: { state: "Arkansas", ...

Oops! You can only have one parent element in JSX expressions

I'm working on creating a password input box, but I keep encountering an error when integrating it into my JS code. Here's the snippet of my code that includes TailwindCSS: function HomePage() { return ( <div> <p className=&q ...

Persist form input data using ajax without any back-end server logic involved

Is there a solution for saving basic form data from a static webpage without using any server-side code? I have thought about potentially using MongoLab through a RESTful interface, but that would mean exposing API credentials on the client side and the ...

Adding a refresh feature to ui-sref in markup

Is there a way to include the reload option in a ui-sref markup without using the javascript function directly? <a ui-sref="app.editPost({new:true}, {reload:true})">new post</a> I've tried this approach, but it doesn't seem to be wo ...

Challenges with importing class-based decorators in Vue using Typescript

Currently, I am in the process of setting up Vue 3 with TypeScript and utilizing class-based components. Unfortunately, I have encountered an issue while attempting to import the Component decorator within the Vue constructor: An error message appears sta ...

navigating to the start of a hyperlink

I'm having issues with scrolling to anchors and encountering 3 specific problems: If I hover over two panels and click a link to one of them, nothing happens. When I'm on section D and click on section C, it scrolls to the end of section C. ...

Joomla template experiencing issues with script loading

I'm currently experimenting with using jQuery to dynamically display content on my Joomla website based on the date. You can check out the working demo on this jsfiddle link: http://jsfiddle.net/2BHLd/968/ $(function() { $(".DateDiv").each(function(i ...

What sets apart assigning a React ref using a callback versus setting it directly?

Is there a practical difference between directly setting a ref and setting it via a callback with the element as an argument? The functionality remains the same, but I am curious about any potential distinctions. Consider this react hook component: const ...

Avoid reloading the page when the form is submitted using form.trigger('submit') command

My current code functions properly when the user clicks on the form's submit button: // load dashboards when filter form is submitted $('div.active form.filter-form').submit(function (e) { // get subm ...

Passing route parameters to child routes in Angular 2: A step-by-step guide

Struggling with passing routing parameters to my component when loaded in a subroute using Angular 2 rc.1 and TypeScript, and utilizing the @angular/router-deprecated package. In the routes configuration of my root component, I have set it up like this: ...

Node.js tutorial: Matching aggregations by UserId

I have been developing a sample application in React where I have utilized aggregations. One of the functionalities involves implementing a query based on user ID that matches the status and retrieves the MAXDate and MINDate fields. How can I retrieve the ...