4

I'm having some bad times while trying to initialize a chart built using ngx-charts with API fetched data.

I built a rest api that, upon a proper call, spits out some time-series data.

{
    "prices": [
        {
            "item": "1",
            "price": 2,
            "estimated": 2.1,
            "date": [
                2012,
                8,
                16
            ]
        },
        {
            "item": "1",
            "price": 3,
            "estimated": 4.1,
            "date": [
                2012,
                9,
                16
            ]
        },
        {
            "item": "1",
            "price": 5,
            "estimated": 7.1,
            "date": [
                2012,
                10,
                16
            ]
        }
    ]
}

And I built price.model.ts to correctly handle it, and it works just fine

export class PriceModel {
    public id: string;
    public price: number;
    public estimated: number;
    public date: number[];

    constructor(
         id: string,
         price: number,
         estimated: number,
         date: number[]
    ) {
        this.id = id;
        this.price = price;
        this.estimated = estimated;
        this.date = date;
     }
}

Then, I set up my details.component.ts in order to perform such api call, get the data, parse it and render it into the chart.

import { Component } from '@angular/core';
import { NgxChartsModule } from '@swimlane/ngx-charts';
import { Http } from '@angular/http';

/** App Models **/
import { PriceModel } from '../../shared/components/prices/price.model';
import { ChartDataModel } from '../../shared/components/prices/chart-data.model';

@Component({
  selector: 'app-details',
  templateUrl: './details.page.html',
  providers: [NgxChartsModule]
})

export class DetailsPage {

  private sub: any;
  private prices: PriceModel[];
  ngxData: ChartDataModel = {
    data: [
      {
        name: 'prices',
        series: []
      },
      {
        name: 'forecast',
        series: []
      }
    ]
  };

  view: any[] = [1000, 750];

  // options
  showXAxis = true;
  showYAxis = true;
  gradient = false;
  showLegend = true;
  showXAxisLabel = true;
  xAxisLabel = 'Dates';
  showYAxisLabel = true;
  yAxisLabel = 'Prices';

  colorScheme = {
    domain: ['#5AA454', '#A10A28', '#C7B42C', '#AAAAAA']
  };

  // line, area
  autoScale = true;

  constructor(private _http: Http) {
    console.log(this.ngxData.data);
    Object.assign(this.ngxData);
  }

  ngOnInit() {
    this.sub = this._http.get('someroute').subscribe((prices) => {
        this.prices = prices.json().prices;
        let currData;
        this.prices.map((p) => {
          currData = new Date(p.date[0], p.date[1], p.date[2]);
          this.ngxData.data[0].series.push({ name: currData.getTime().toString(), value: p.price });
          this.ngxData.data[1].series.push({ name: currData.getTime().toString(), value: p.estimated });
        });
      }, (err) => {
        console.log(err);
    });
  }

  ngOnDestroy() {
    this.sub.unsubscribe();
  }

}

Where my ChartDataModel.ts is defined as

export class ChartDataModel {
    public data: SerieModel[];
    constructor(data:  SerieModel[]) {
        this.data = data;
    }
}

export class SerieModel {
    public name: string;
    public series: SeriersChildModel[];
    constructor(name:  string, series: SeriersChildModel[]) {
        this.name = name;
        this.series = series;
    }
}

export class SeriersChildModel {
    public name: string;
    public value: number;
    constructor(name:  string, value: number) {
        this.name = name;
        this.value = value;
    }
}

And, finally, here's my details.page.html

<ngx-charts-line-chart
  [view]="view"
  [scheme]="colorScheme"
  [results]="ngxData.data"
  [gradient]="gradient"
  [xAxis]="showXAxis"
  [yAxis]="showYAxis"
  [legend]="showLegend"
  [showXAxisLabel]="showXAxisLabel"
  [showYAxisLabel]="showYAxisLabel"
  [xAxisLabel]="xAxisLabel"
  [yAxisLabel]="yAxisLabel"
  [autoScale]="autoScale">
</ngx-charts-line-chart>

Logging this.ngxData.data before Object.assign prints everything just fine

enter image description here

But I end up having the following result

enter image description here

I followed the example available here but ended up with no data actually displaying.

I don't understand why, even though data is formatted as the library wants, data isn't shown.

What Am I doing wrong? Is it caused by a wrong initialization in the constructor?

AndreaM16
  • 3,917
  • 4
  • 35
  • 74
  • There is quite a lot of code posted here (I'll try to read it anyway), however you may benefit from trying to produce an [mcve]. Or add a [plunker example](https://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5?p=catalogue) demonstrating the issue, to make it easier to answer. – 0mpurdy Aug 06 '17 at 13:10
  • Hi @0mpurdy, yes, a Plunk would be really nice but, unluckily, I cannot set up this piece in there since my BE is in local and would be a mess for headers etc. – AndreaM16 Aug 06 '17 at 13:11
  • Does your details.component.ts render the chart with static data e.g. in a unit test? Just to make sure that ngx-charts is properly imported, set up etc. – Kim Kern Aug 06 '17 at 13:20
  • Yes @KimKern, using static data it works just fine. – AndreaM16 Aug 06 '17 at 13:24
  • You can mock the data coming back in [a plunker like this](https://plnkr.co/edit/4Mrgrot568OsekCQfKtn?p=preview), can you confirm with a `console.log` inside your subscription to check that the API is returning the data correctly? – 0mpurdy Aug 06 '17 at 14:22
  • I forked it. Yes, the data shown is correct. – AndreaM16 Aug 06 '17 at 14:33

2 Answers2

14

Your issue is about modifying series by ngxData.data[x]series.push() is not recognizing by change detection.

Reassigning ngxData.data should be detected : this.ngxData.data = [...this.ngxData.data]

ngOnInit() {     
    this.sub = this._http.get('someroute').subscribe((prices) => {
        this.prices = prices.json().prices;
        let currData;
        this.prices.map((p) => {
          currData = new Date(p.date[0], p.date[1], p.date[2]);
          this.ngxData.data[0].series.push({ name: currData.getTime().toString(), value: p.price });
          this.ngxData.data[1].series.push({ name: currData.getTime().toString(), value: p.estimated })
        });
        //your solution
        this.ngxData.data = [...this.ngxData.data];
      }, (err) => {
        console.log(err);
    });
  }

I managed to add a plunker https://plnkr.co/edit/JSTcS4FnJ5dshAldLWPL?p=preview

Fetrarij
  • 7,176
  • 3
  • 27
  • 35
0

For my USE CASE ....

first you need to initialize two arrays

Then you need to call your service and for every iteration, we will add object to saleData array

customers: Customer[] = [];
  saleData: any = [];
    ngOnInit(): void {
        this.customerService.getTop10Customers().subscribe(data => {
          this.customers = data._embedded.customers
          console.log(this.customers)
          let sales:any = [];
           this.customers.forEach(c =>{
             sales.push({name: c.designation,value: c.totalMoneySpent})
           })
          // clone the sales array to saleData 
          this.saleData = [...sales]
          console.log(this.saleData)
    
        })
      }

And this is the result:

enter image description here

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459