ngx pie chart - unable to access input data inside labelFormatting

87 views Asked by At

I'm failing to access my portfolio input property inside the labelFormatting method.

I need this portfolio data in order to calculate the sum, so as to format the label data into percentages.

The problem I'm facing is that the portfolio object is persistently undefined when called within the context of the labelFormatting. I'm assuming this happens because this method is called before the input property is ready, when the chart is first created.

Here is my component:

export class PortfolioPreviewCardComponent {
    @Input() portfolio!: IPortfolio;
    data = [...];

    // options
    showLegend = false;
    showLabels = true;
    isDoughnut = true;

    ...
    ...
    ...

    labelFormatting(name: string) { // this name will contain the name you defined in chartData[]
        const self: any = this; // this "this" will refer to the chart component
        const data: DataItem[] = self.series.filter((x: { name: string }) => x.name == name);
    
        // portfolio is undefined here
        const sum = this.portfolio.holdings.reduce((accumulator, item) => {
            return accumulator + item.total;
        }, 0);

        if(data.length > 0) {
            return (((data[0].value / sum) * 100).toFixed(2) + '%');
        } else {
            return name;
        }
    }
}

Here is the template:

<ngx-charts-pie-chart
        *ngIf="portfolio"
        scheme="cool"
        [results]="data"
        [legend]="showLegend"
        [labels]="showLabels"
        [doughnut]="isDoughnut"
        [labelFormatting]="labelFormatting"
        (select)="onSelect($event)"
        (activate)="onActivate($event)"
        (deactivate)="onDeactivate($event)"
>
</ngx-charts-pie-chart>
2

There are 2 answers

0
Manuel Brás On BEST ANSWER

I mananged to find a solution where I explicitly set the labelFormatting function of the chart in the AfterViewInit hook, like so:

@ViewChild('pieChart') pieChart!: PieChartComponent; // template chart reference
...
...

ngAfterViewInit(): void {
    if (this.pieChart) {
        // used a chartManager service to deal with the labelFormatting logic
        this.pieChart.labelFormatting = (name: string) => this.chartManager.labelFormatting(name);
    }
}
1
vcaraccio On

As you commented in your code, this is the chart in your handler. Perhaps you should just return a function to keep your component instance in a closure?

labelFormatting() {
    const myComponent = this;
    return (name: string) => {
        const data: DataItem[] = this.series.filter((x: { name: string }) => x.name == name);

        const sum = myComponent.portfolio.holdings.reduce((accumulator, item) => accumulator + item.total, 0);

        if (data.length > 0) {
            return (((data[0].value / sum) * 100).toFixed(2) + '%');
        }

        return name;
    }
}

The your attribute should be like this:

<ngx-charts-pie-chart
    [labelFormatting]="labelFormatting()"></ngx-charts-pie-chart>