Ng2-charts: Cannot read property 'data' of undefined

Created on 23 Sep 2016  路  25Comments  路  Source: valor-software/ng2-charts

After updating from 1.2 to 1.3.

This line of code
this.chart.data.datasets = this.getDatasets();
gives me error when I show chart:

Cannot read property 'data' of undefined

Tring to reproduce the issue in plunker, got no luck so far...

Most helpful comment

+1 i run in the same issue.

I'm using rxjs observables with angular2 and even if i provide some default empty data fot chart.data i'm still facing errors.

code very basic:

this.myService.getMyData()
      .subscribe(value=> {
        this.ChartData = [
          { data: value.data1, label: 'data1' },
          { data: value.data2, label: 'data2' }
        ];
      });

Error: Cannot read property 'data' of undefined

UPDATE:
okey i just solved it with technic of using *ngIf. I hide chart component till i get data and then show it.

All 25 comments

Ok, I got some time to look into this.

The issue can be reproduced by adding a setTimeout.

setTimeout(() => {
  this.datasets = [{
    label: "# of Votes",
    data: [12, 19, 3, 5, 2, 3]
  }];
}, 100);

The issue is because of lack of [{ data: [] }]; in the beginning.

In 1.2, it works this way in a child component.

@Input() datasets: any[] = [{ data: [] }];

Then you can provide data later.

But 1.3, that way does not work any more. Since I am using ngrx/store, so I changed the way to give that default data in reducer:

const initialState = {
  datasets: [{ data: [] }]
};

At least, it works now, I will close now.

I just ran into this issue in my application as well, although I am not sure how to solve it in my case.

where exactly did you add the code above? To your code or the angular2 charts code?

my charts were working just fine and now that I have upgraded I have

 <base-chart *ngIf="!Widget.gridview.show" class="chart" id="chart_{{Widget.id}}"
                        [data]="Widget.chart.data"
                        [datasets]="Widget.chart.datasets"
                        [labels]="Widget.chart.labels"
                        [options]="Widget.chart.options"
                        [legend]="Widget.chart.legend"
                        [chartType]="Widget.chart.chartType"
                        (chartClick)="chartClicked($event)"></base-chart>

and it throws the exception Cannot read property 'data' of undefined

please advise!

thanks in advance

@blubberbo I am using ngrx/store, so I added it in the initialState, which makes sure datasets initial value is [{ data: [] }] before getting data from server.

@Hongbo-Miao ah, alright. I am not, I am passing a variable Widget to the ngFor the base-chart element is in. So basically, if I initialize the value Widget.chart.data to nothing, like this:

Widget.chart.data = []

before passing it onto the page, it should then work?

@blubberbo u need at least provide [{ data: [] }], like this Widget.chart.data = [{ data: [] }]

+1 i run in the same issue.

I'm using rxjs observables with angular2 and even if i provide some default empty data fot chart.data i'm still facing errors.

code very basic:

this.myService.getMyData()
      .subscribe(value=> {
        this.ChartData = [
          { data: value.data1, label: 'data1' },
          { data: value.data2, label: 'data2' }
        ];
      });

Error: Cannot read property 'data' of undefined

UPDATE:
okey i just solved it with technic of using *ngIf. I hide chart component till i get data and then show it.

@szykov great idea! 屑芯谢芯写械褑

I'm also having this issue, although not with using initial data.
When updating the chart, data including label count, I'm using ViewChild / chart.ngOnChanges().
issue ref here: #692

This works when going from a set of series to higher count, but not the other way, it throws a data undefined error.

specifically here:

BaseChartDirective.prototype.updateChartData = function (newDataValues) {
            if (Array.isArray(newDataValues[0].data)) {
                this.chart.data.datasets.forEach(function (dataset, i) {
                    dataset.data = newDataValues[i].data;
                    if (newDataValues[i].label) {
                        dataset.label = newDataValues[i].label;
                    }

(current "datasets" array count differ from newDataValues)

I'm using ng2-charts 1.6.0 and chart.js 2.5.0

Any ideas of a workaround to this?

@nniclas Im facing the same issue too. Let me know if you have found any workarounds.

@valorkin Are you guys planning to handle dynamic labels and dynamic sizes of datasets anytime in the future for ng2-charts?

@nponna Unfortunately I don't have any solid workaround, although I did modify updateChartData() in ng2-charts.js

from

        BaseChartDirective.prototype.updateChartData = function (newDataValues) {
            if (Array.isArray(newDataValues[0].data)) {
                this.chart.data.datasets.forEach(function (dataset, i) {
                    dataset.data = newDataValues[i].data;
                    if (newDataValues[i].label) {
                        dataset.label = newDataValues[i].label;
                    }
                });
            }
            else {
                this.chart.data.datasets[0].data = newDataValues;
            }
        };

to

        BaseChartDirective.prototype.updateChartData = function (newDataValues) {
            if (Array.isArray(newDataValues[0].data)) {
                this.chart.data.datasets.forEach(function (dataset, i) {
                    if (newDataValues[i]) {  // skip if new array count is lower
                        dataset.data = newDataValues[i].data;
                        if (newDataValues[i].label) {
                            dataset.label = newDataValues[i].label;
                        }
                    }
                });
            }
            else {
                this.chart.data.datasets[0].data = newDataValues;
            }
        };

this seems to work without any errors and side effects, (so far..)

I have no experience in extending and modifying libraries, appreciate if anyone could guide me how to override this function so it builds with ng2-charts?
Is it possible to implement this fix project-only wise without forking ng2-charts?

Ok so I solved this, I just added a file and referenced BaseChartDirective from ng2-charts, had to require ng2-charts..

 var ng2charts = require('ng2-charts');
    ng2charts.BaseChartDirective.prototype.updateChartData = function (newDataValues) 
    { // new code }

this fix works and builds with webpack

i am facing the same issue "(current "datasets" array count differ from newDataValues)",changing in charts.js works for me but i dont want to change that.
can you provide the alternative way in detail @nniclas .
i am not getting your below mentioned solution(As i am new to charts.js) it is giving require not found and many issues :
var ng2charts = require('ng2-charts');
ng2charts.BaseChartDirective.prototype.updateChartData = function (newDataValues)
{ // new code }

can you provide the way to override the existing code of updateChartData in my solution or component itself.

I'm having the same issue moving to a dataset with less values, overwriting updateChartData didn't work, destroying the chart instance doesn't work. Any plans for a fix?

szykov's solution is great. use the following should work:
"

Hello! I was having the same problem (I guess). Dataset is refreshed from ngOnChanges method. Dataset can be filtered and after reducing the number of data the error Cannot read property 'data' of undefined was thrown.

I came to the @szykov solution *ngif.. as well

<div *ngIf="barChartData.length > 0">
  <canvas
    baseChart 
    [datasets]="barChartData" ...

However that wasn't enough for me. What I did as a workaround to properly refresh the chart is clear the barCharData and wait a tick to refresh dataset:

ngOnChanges(changes) {
  this.barChartData = [];
  Promise.resolve(true).then(() => this.refresh());
}

refresh() {
  this.barChartData = ...
}

This worked for me, editing ng2-charts.umd.js (or /charts/charts.js for some):
BaseChartDirective.prototype.ngOnChanges = function (changes) {
if (this.initFlag) {
// Check if the changes are in the data or datasets
// console.log(changes);
// console.log(changes.currentValue);
if (changes.hasOwnProperty('data') || changes.hasOwnProperty('datasets')) {
if (changes['data']) {
this.updateChartData(changes['data'].currentValue, changes['data'].currentValue.length);
}
else {
// console.log(changes['datasets'].currentValue);
this.updateChartData(changes['datasets'].currentValue, changes['datasets'].currentValue.length);
// console.log(this);
}
// add label change detection every time
if (changes['labels']) {
if (this.chart && this.chart.data && this.chart.data.labels) {
this.chart.data.labels = changes['labels'].currentValue;
}
}
this.chart.update();
}
else {
// otherwise rebuild the chart
this.refresh();
}
}
};

BaseChartDirective.prototype.updateChartData = function (newDataValues, lengthOfChanges) {

        var colors = [
            [255, 99, 132],
            [54, 162, 235],
            [255, 206, 86],
            [231, 233, 237],
            [75, 192, 192],
            [151, 187, 205],
            [220, 220, 220],
            [247, 70, 74],
            [70, 191, 189],
            [253, 180, 92],
            [148, 159, 177],
            [77, 83, 96]
        ];
            for (var i = 0; i < lengthOfChanges; i++) {
                    newDataValues[i].backgroundColor = "rgba("+colors[i][0]+","+colors[i][1]+","+colors[i][2]+", 0.4)";
                    newDataValues[i].borderColor = "rgba("+colors[i][0]+","+colors[i][1]+","+colors[i][2]+", 1)";
                    newDataValues[i].borderColor = "rgba("+colors[i][0]+","+colors[i][1]+","+colors[i][2]+", 0.8)";
                    newDataValues[i].pointBackgroundColor = "rgba("+colors[i][0]+","+colors[i][1]+","+colors[i][2]+", 1)";
                    newDataValues[i].pointBorderColor = "#fff";
                    newDataValues[i].pointHoverBackgroundColor = "#fff";
            }
            this.chart.data.datasets = newDataValues;

};

Hi, I have data like this

`dataCharts = [{"value":54,"ru":"R201","area":"HCC"},{"value":1,"ru":"R401","area":"HOC"},{"value":3,"ru":"R401","area":"LWSR"}];

getStatsMonthly() {
this.authProvider.getDataChartMonthly().then(data => {
this.dataCharts = data;
this.datasetMonthly = [];
let res = [];
let dataValue: any = [];
dataValue = [];

  res = Object.values(this.dataCharts.reduce((obj, curr) => {
  if (!obj[curr.ru]) obj[curr.ru] = {ru: curr.ru, area: [],value:[]};
    // push the current name into the current time entry
    obj[curr.ru]['area'].push(curr.area);
    obj[curr.ru]['value'].push(curr.value);
    return obj;
  }, {}));

  for (let a of res) {
    // Get label chart data
    this.labelMonthly.push(a.ru);
  }

  for (let a of res) {
    for (let r of a.area) {
      // Get data area
    }
    const reducer = (values, currentValue) => values + currentValue;
    // Get data value
    setTimeout(() => {
      this.datasetMonthly.push(a.value.reduce(reducer));
    }, 300);
  }
});

}
`
The error says ERROR TypeError: Cannot read property 'data' of undefined, could someone help this?

just use:
this.lineChartLabels = this.lineChartLabels.slice(0);
this.lineChartData[0]['data'] = this.lineChartData[0]['data'].slice(0);;

+1 i run in the same issue.

I'm using rxjs observables with angular2 and even if i provide some default empty data fot chart.data i'm still facing errors.

code very basic:

this.myService.getMyData()
      .subscribe(value=> {
        this.ChartData = [
          { data: value.data1, label: 'data1' },
          { data: value.data2, label: 'data2' }
        ];
      });

Error: Cannot read property 'data' of undefined

UPDATE:
okey i just solved it with technic of using *ngIf. I hide chart component till i get data and then show it.

Finally after struggling for a half day with this issue. Why ng2chart doesn't provide this example? Many thanks brotha!!!

@blubberbo u need at least provide [{ data: [] }], like this Widget.chart.data = [{ data: [] }]

thanks! but I have to say this is not good at all

+1 i run in the same issue.

I'm using rxjs observables with angular2 and even if i provide some default empty data fot chart.data i'm still facing errors.

code very basic:

this.myService.getMyData()
      .subscribe(value=> {
        this.ChartData = [
          { data: value.data1, label: 'data1' },
          { data: value.data2, label: 'data2' }
        ];
      });

Error: Cannot read property 'data' of undefined

UPDATE:
okey i just solved it with technic of using *ngIf. I hide chart component till i get data and then show it.

You could have also put the chart code inside the subscribe method itself. So that it could render the chart after gettting data. All of this in the chart component ts file.

Works like charm!

I was getting this issue when providing labels initially in the labels property then later assigning dataset with elements that also had a labels.

This worked for me [datasets]="[{ data: [] }] || chartData.datasets It is like having an initial state before the actual data pops in.

@blubberbo u need at least provide [{ data: [] }], like this Widget.chart.data = [{ data: [] }]

Thanks. This works perfectly.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

tushartgsit picture tushartgsit  路  5Comments

brandonreid picture brandonreid  路  3Comments

mrpotato3 picture mrpotato3  路  5Comments

shyamal890 picture shyamal890  路  4Comments

alexcastillo picture alexcastillo  路  5Comments