Issue
I'm trying to create simple CRUD database with Spring Boot and Angular using REST requests and draw some charts out of it. Spring boot sends this after GET request:
"name" : "COVID",
"infected": 500,
"simulationTime": 3,
"rvalue": 1.2,
"dailyStatsList": [
{
"day": 1,
"pv": 9500,
"pr": 0,
"pm": 0,
"pi": 500
},
{
"day": 2,
"pv": 9392,
"pr": 0,
"pm": 0,
"pi": 608
},
{
"day": 3,
"pv": 9260,
"pr": 0,
"pm": 0,
"pi": 740
}
]
}
This is my component.ts file:
export class SimulationDetailsComponent implements OnInit {
currentSimulation!: SimulationWithStats;
chartData = [
{ data: [10, 20, 30, 40, 50], label: 'Value1' },
{ data: [], label: 'Value2' },
{ data: [], label: 'Value3' },
];
public chartLabel: string[] = ['1', '2', '3', '4', '5'];
lineChartOptions = {
responsive: true,
};
lineChartColors: Color[] = [
{
borderColor: 'black',
backgroundColor: 'rgba(255,255,0,0.28)',
},
];
lineChartLegend = true;
lineChartPlugins = [];
lineChartType: ChartType = 'line';
constructor(
private controllerService: ControllerService,
private route: ActivatedRoute,
private router: Router
) {}
ngOnInit(): void {
var id = this.route.snapshot.params['id'];
this.getSimulation(id);
for (let i = 0; i < 5; i++) { //THIS ONE WORKS FINE
this.chartData[1].data.push(i * 2);
}
for (let dailyStats of this.currentSimulation.dailyStatsList) { //THIS DOESN'T WORK
this.chartData[2].data.push(dailyStats.pi);
}
}
getSimulation(id: string): void {
this.controllerService.get(id).subscribe(
(data) => {
this.currentSimulation = data;
console.log(data);
},
(error) => {
console.log(error);
}
);
}
}
Unfortunately, it doesn't work. It looks like currentSimulation isn't initialized at the time I want to fill my chartData[]. I admit, that currentSimulation initalizes fine, because I can print all of the data on html page. What might be the problem? Do you think that I should wait some time before chartData[] filling?
Solution
The problem is that you're not awaiting for the http call to return the actual data.
Change the method getSimulation
to look like this:
async getSimulation(id: string): Promise<void> {
try {
this.currentSimulation = await this.controllerService.get(id).toPromise();
console.log('current simulation data retrieved successfully', this.currentSimulation);
} catch (error) {
console.error('error retrieving current simulation', error);
}
}
And then also do this adjustments to ngOnInit()
.
async ngOnInit(): Promise<void> {
var id = this.route.snapshot.params['id'];
await this.getSimulation(id);
.............................
}
This should do the trick.
By the way, I don't know what version of angular you're using, that's why I used .toPromise()
for casting from Observable to Promise. However on the recent versions of Angular (IIRC Angular 11+), lastValueFrom
is the one to use, since .toPromise()
was deprecated and will be removed sooner than later.
So getSimulation
would ideally look like this:
async getSimulation(id: string): Promise<void> {
try {
this.currentSimulation = await lastValueFrom(this.controllerService.get(id));
console.log('current simulation data retrieved successfully', this.currentSimulation);
} catch (error) {
console.error('error retrieving current simulation', error);
}
}
Answered By - Mihai Paraschivescu