1

I have two functions in Angular:

One to get some data from a web service and store it in the this.apiDay and this.apiDayLabel variable:

getDayScan() {
    this.btcPriceService.getDailyBTCScan().subscribe(data => {
      data.Data.Data.forEach(price => {
        this.apiDay.push(price.open);
        this.apiDayLabel.push(new Date(price.time * 1000).toLocaleTimeString([], {hour: '2-digit', minute: '2-digit'}));
      });
    });
}

and one to create a chartjs with the data from this.apiDay and this.apiDayLabel :

public createDay(defaultChartConfig: any) {

    this.canvas = document.getElementById('dayChart');
    this.ctx = this.canvas.getContext('2d');

    const dataTotal = {
      // Total Shipments
      labels: this.apiDayLabel,
      datasets: [{
        label: 'Price',
        fill: true,
        backgroundColor: this.bgColorSelector(this.apiDay),
        borderColor: this.borderColorSelector(this.apiDay),
        borderWidth: 2,
        borderDash: [],
        borderDashOffset: 0.0,
        pointBackgroundColor: this.borderColorSelector(this.apiDay),
        pointBorderColor: 'rgba(255,255,255,0)',
        pointHoverBackgroundColor: this.borderColorSelector(this.apiDay),
        pointBorderWidth: 20,
        pointHoverRadius: 4,
        pointHoverBorderWidth: 15,
        pointRadius: 0,
        data: this.apiDay,
      }]
    };

    this.myChartDay = new Chart(this.ctx, {
      type: 'lineWithLine',
      data: dataTotal,
      options: defaultChartConfig
    });
  }

I call these two functions in the ngOnInit() function like this:

ngOnInit() {
    this.getDayScan();
    this.createDay(defaultChartConfig);
}

My problem is that the chart is created before I have my data from the api.

Is there a way to wait for the data to be there and then start creating the chart?

Like so (Pseudocode)

public createDay(defaultChartConfig: any) {

    getDayScan();

    // wait for it to finish so every necessary variable is declared
    // and only THEN go on with the other code 

    this.canvas = document.getElementById('dayChart');
    this.ctx = this.canvas.getContext('2d');

    ...

}

So I have to call only the createDay function in the ngOnInit

Or what is best practice in this case?

8
  • The best practice is to draw the chart in the callback when you get the data Commented Mar 18, 2020 at 7:21
  • I think not because I have a chart which uses multiple apis Commented Mar 19, 2020 at 6:10
  • Have you tried to call this.createDay(defaultChartConfig); inside this.getDayScan(); when you got response from api. Commented Mar 20, 2020 at 8:25
  • yes generally this is working BUT I have one chart which uses multiple apis this means when I do it like this the data of the first api is set but not of the second and my chart is incomplete Commented Mar 20, 2020 at 8:28
  • because of that I want it to be in the create function there I can say get ALL api data and set the variables I need and then create this specific chart Commented Mar 20, 2020 at 8:30

2 Answers 2

4
+50

You can fill chart data after getting a response from API.

Create one common function for multiple calls at the same time.

getJoin(URL_Array: Array<string>): Observable<any> {
    const observableBatch = [];
    URL_Array.forEach((url) => {
      observableBatch.push(this._httpClient.get<any>(`${API_URL}${url}`)
        .pipe(map(res => { return res; })));
    });
    return forkJoin(observableBatch);
};


getDayScan() {
  const urls = [
     this.btcPriceService.getDailyBTCScan1(), // API 1 URL
     this.btcPriceService.getDailyBTCScan2()  // API 2 URL
  ];
    this.btcPriceService.getJoin(urls)
     .subscribe(res => {
        // You will get array inside res with the same number of the array that you passed API. So Use for loop for res.

        for (let i = 0; i < res.length; i++) { // here will be res.length is 2 in this example
            const element = res[i];

            // Apply your logic here if you got different response in different API
            element.Data.Data.forEach(price => {
               this.apiDay.push(price.open);
               this.apiDayLabel.push(new Date(price.time * 1000)
               .toLocaleTimeString([], {hour: '2-digit', minute: '2-digit'}));
            });
        }

        // Call chart function after loop complete
        this.createDay(defaultChartConfig);            
    });
}

createDay(defaultChartConfig: any) {
   this.canvas = document.getElementById('dayChart');
   this.ctx = this.canvas.getContext('2d');

   const dataTotal = {
     // Total Shipments
     labels: this.apiDayLabel,
     datasets: [{
       label: 'Price',
       fill: true,
       backgroundColor: this.bgColorSelector(this.apiDay),
       borderColor: this.borderColorSelector(this.apiDay),
       borderWidth: 2,
       borderDash: [],
       borderDashOffset: 0.0,
       pointBackgroundColor: this.borderColorSelector(this.apiDay),
       pointBorderColor: 'rgba(255,255,255,0)',
       pointHoverBackgroundColor: this.borderColorSelector(this.apiDay),
       pointBorderWidth: 20,
       pointHoverRadius: 4,
       pointHoverBorderWidth: 15,
       pointRadius: 0,
       data: this.apiDay,
    }]
  };

  this.myChartDay = new Chart(this.ctx, {
     type: 'lineWithLine',
     data: dataTotal,
     options: defaultChartConfig
   });
}
Sign up to request clarification or add additional context in comments.

Comments

0

I suggest you to use async await to wait for the api response.

 async getDayScan() {
        await this.btcPriceService.getDailyBTCScan().subscribe(data => {
          data.Data.Data.forEach(price => {
            this.apiDay.push(price.open);
            this.apiDayLabel.push(new Date(price.time * 1000).toLocaleTimeString([], {hour: '2-digit', minute: '2-digit'}));
          });
        });
    }

call the getDayScan() from createDay(),

public async createDay(defaultChartConfig: any) {

    await getDayScan();

    // wait for it to finish so every necessary variable is declared
    // and only THEN go on with the other code 

    this.canvas = document.getElementById('dayChart');
    this.ctx = this.canvas.getContext('2d');

    ...

}

You need to call only the createDay() from ngOnInit()

If you don't like to use async await, use this working example.

Sorry for any typo.

2 Comments

same behaviour as before
To use with return await with observable not make sense. You directly use the promise for that.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.