0

I'm using highcharts in Angular 2. If I use static data the chart renders as expected, but if I use data from a http.get request in my service and use a subscribe to get it to my component it does not render. What returns instead is a empty chart. I can see from the console.log that the data is returning as expected to the component. I'm not sure what I'm doing wrong. Below is the relevant code. Thank you in advance.

Service

const API_URL = 'https://api.blockchain.info/price/index-series?base=btc&quote=USD&start=1503145039&scale=7200';

@Injectable()
export class BitcoinPriceService {

loadstate: boolean;

stateChange: Subject<boolean> = new Subject<boolean>();

moneyArray = [];

moneyChange: Subject<any> = new Subject<any>();

private getArray(): void {
// this.moneyChange.next(this.moneyArray);
}

constructor(private http: Http) {
    this.loadstate = false;
}

private showLoader(): void {
  this.loadstate = true;
  this.stateChange.next(this.loadstate);
}

private hideLoader(): void {
  this.loadstate = false;
  this.stateChange.next(this.loadstate);
}

    getData(url = API_URL) {
    this.showLoader();
    return this.http.get(API_URL)
        .subscribe((res: Response) => {
            let results = this.moneyArray;
            let obj = res.json();
            obj.forEach(
                 function (o: any) {
                    results.push(o);
                }
            );
        })
// .error(err => {
        //   console.error('handling error within getData()', err);
        //   const fakeData = [{ name: 'no prices could be retrieved' }];
        //   return Observable.of(fakeData);
        // })
        // .finally(() => {
        //      this.getArray();
//        this.hideLoader();

//        console.log('hideloader', this.loadstate);
//        console.log(this.moneyArray);
//    });
} 

}

Component

export class ChartComponent implements OnInit {

priceData: Subscription;

loadstate: boolean;
subscription: Subscription;

moneyArray = [];

constructor(bs: BitcoinPriceService) {
    console.log(bs);
    this.priceData = bs.getData();
    this.loadstate = bs.loadstate
    this.subscription = bs.stateChange.subscribe((value) => { 
      this.loadstate = value; 
    });



    console.log('moneyArray', this.moneyArray = bs.moneyArray);

    console.log('priceData', this.priceData);
    console.log('loadstate', this.loadstate);
}


private data = [
    {
        name: 'USA',
        data: this.moneyArray
    }, 
    {
        name: 'USSR/Russia',
        data: this.moneyArray,
    }];

ngAfterViewInit() {
    this.renderChart();
}

renderChart(){
    jQuery('#container').highcharts({
        chart: {
            type: 'area'
        },
        title: {
            text: 'Bitcoin Price'
        },
        subtitle: {
            text: 'Source: thebulletin.metapress.com'
        },
        xAxis: {
            allowDecimals: false,
            labels: {
                formatter: function () {
                    return this.value;
                }
            }
        },
        yAxis: {
            title: {
                text: 'Nuclear weapon states'
            },
            labels: {
                formatter: function () {
                    return this.value / 1000 + 'k';
                }
            }
        },
        tooltip: {
            pointFormat: '{series.name} produced <b>{point.y:,.0f}</b>' +
                         '<br/>warheads in {point.x}'
        },
        plotOptions: {
            area: {
                pointStart: 1940,
                marker: {
                    enabled: false,
                    symbol: 'circle',
                    radius: 2,
                    states: {
                        hover: {
                            enabled: true
                        }
                    }
                }
            }
        },
        series: [{
            data: this.data,
            name: 'Value Type Description'
        }]
    });
}

ngOnDestroy() {
    //prevent memory leak when component destroyed
    this.subscription.unsubscribe();
}
}

This is how the JSON data is being returned to the moneyArray. enter image description here

London804
  • 1,072
  • 1
  • 22
  • 47
  • You need to understand asynchronism. You're trying to eat a toast right after you've put it in the toaster. That can't work. You need to get the toast from th toaster when the toaster tells you it's ready. Stop subscribing inside the service. Your service must return an Observable. You need to subscribe to that Observable from the component. Then, n the subscribe callback, that is called when the observable gives you the data that was loaded asynchronously, the data are available, and you can build your chart with that data. `service.getData().subscribe(data => buildChartWithData(data))`). – JB Nizet Nov 24 '17 at 20:10
  • Thanks, but this isn't completely clear to me. So I have to put my subscribe inside of the component, but what does "buildChartWithData(data) represent? – London804 Nov 24 '17 at 20:38
  • The code that builds your chart with the data you obtained from the observable event. – JB Nizet Nov 24 '17 at 20:41
  • When I console.log the data coming from the service. It returns undefined. However, when I console.log the service I see the data on the service object. – London804 Nov 24 '17 at 22:30
  • 2
    That's because you return data before they are available. Asynchronism. You absolutely need to understand this concept. Sending an HTTP request doesn't block and wait until the response is available. It returns immediately. Much later, the callback passed to subscribe is being called. Again, don't return data from your service. Don't store data in your service. Return an Observable. And subscribe to it in the component. And only create the chart when the data is available, i.e. in the callback passed to subscribe. – JB Nizet Nov 24 '17 at 22:42

1 Answers1

0

I think you should use 'rxjs/add/operator/map';

import 'rxjs/add/operator/map';

constructor(bs: BitcoinPriceService, private zone: NgZone) {
  console.log(bs);
  this.priceData = bs.getData().map(resp => {
    //You can init your chart here
   };
  );
  this.loadstate = bs.loadstate
  this.subscription = bs.stateChange.subscribe((value) => { 
    this.loadstate = value; 
  });
}

Also it will be better to write Api call inside a separate function and call it in ngOnInit()

Ulrich Dohou
  • 1,509
  • 14
  • 25