Reputation: 368
Before raising this question, I have gone through the similar issues raised, but no luck in fixing my issue. So I'm raising this question again to understand on why exactly this is happening.
I started creating one web application where in one of my page users can download data by just clicking the button provided. Everything worked fine till I started using FOR loop to get some additional data and adding it to new array. Here is my code for better explaination.
employee-list.component.ts
import { Component, OnInit } from '@angular/core';
import { RestApiService } from "../shared/rest-api.service";
@Component({
selector: 'app-employees-list',
templateUrl: './employees-list.component.html',
styleUrls: ['./employees-list.component.css']
})
export class EmployeesListComponent implements OnInit {
term: string;
Employee: any = [];
assets: any = [];
reportData: any = [];
constructor(
public restApi: RestApiService
) { }
ngOnInit() {
this.loadEmployees()
}
// Get employees list
loadEmployees() {
return this.restApi.getEmployees().subscribe((data: {}) => {
this.Employee = data;
})
}
//Download Employee report
download() {
let fileName = 'EmployeeReport.csv';
let columnNames = ["Emp ID", "Employee ID", "First Name", "Last Name","Email","Department ID","Designation",
"Emp Active","Inventory ID","Device Name","Device Type ID","Device Type Name","Serial No","Model","Processor",
"RAm ID","HDD ID","OS ID","Warranty Date","Purchase Date","Description","Repair","Inv Active","RAM Size","HDD Size","OS Name","Asset Status"];
let header = columnNames.join(',');
let csv = header;
csv += '\r\n';
####create an reportData array####
//*******parent for loop**********
for(let emp of this.Employee){
this.restApi.getEmployeeAsset(emp.id).subscribe((data: {}) => {
this.assets = data;
if(this.assets.length !== 0){
//*****child for loop****
for(let asset of this.assets){
this.reportData.push({"eid": emp.id,"employeeId":emp.employee_id,"firstName":emp.first_name,"lastName":emp.last_name,"email":emp.email, "departmentId":emp.department_id,"designation":emp.designation,"eActive":emp.is_active,"invId":asset.id,"deviceName":asset.device_name, "deviceTypeId":asset.device_type_id,"deviceTypeName":asset.device_type_name,"serialNo":asset.serial_no,"model":asset.model,"processor":asset.processor, "ramId":asset.ram_id,"hddId":asset.hdd_id,"osId":asset.os_id,"warrantyDate":asset.warranty_date,"purchaseDate":asset.purchased_date, "description":asset.description,"repair":asset.repair,"invActive":asset.is_active,"ramSize":asset.ram_size,"hddSize":asset.hdd_size,"osName":asset.os_name,"assetStatus":asset.asset_status})
}
}
else {
this.reportData.push({"eid": emp.id,"employeeId":emp.employee_id,"firstName":emp.first_name,"lastName":emp.last_name,"email":emp.email,
"departmentId":emp.department_id,"designation":emp.designation,"eActive":emp.is_active})
}
console.log("Report Data is available here"+JSON.stringify(this.reportData))
})
console.log("Report data is empty here"+JSON.stringify(this.reportData))
}
this.reportData.map(c => {csv += [c["eid"], c["employeeId"],c["firstName"],c["lastName"],c["email"],c["departmentId"],c["designation"],c["eActive"], c["invId"],c["deviceName"],c["deviceTypeId"],c["deviceTypeName"],c["serialNo"],c["model"],c["processor"],c["ramId"], c["hddId"],c["osId"],c["warrantyDate"],c["purchaseDate"],c["description"],c["repair"],c["invActive"],c["ramSize"],c["hddSize"],c["osName"],c["assetStatus"]].join(',');
csv += '\r\n';
})
var blob = new Blob([csv], { type: "text/csv;charset=utf-8;" });
var link = document.createElement("a");
if (link.download !== undefined) {
var url = URL.createObjectURL(blob);
link.setAttribute("href", url);
link.setAttribute("download", fileName);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
}
}
Here in this code, under download()
method, I'm creating an array reportData
which later I'm converting it to csv format which can be downloaded.
But this particular array reportData
is empty once we are out of this.restApi.getEmployeeAsset(emp.id).subscribe
call with in the parent for loop
. This seems to be weird for me and I'm really not understanding why this is happening.
I found this by logging results to the console at 2 different places with in the for loop.
one is console.log("Report Data is available here"+JSON.stringify(this.reportData))
with in the subscribe method of API call where I can see the reportData
and another one is console.log("Report data is empty here"+JSON.stringify(this.reportData))
where reportData
is empty.
Can someone please explain what mistake I'm doing here and why exactly this is happening?
I don't have any errors showing and I was able to download the required file, but with empty data which has only Header column. :(
Work around that currently I had in place
I placed the For loop logic
from download()
to loadEmployees()
which in turn is being called under ngOnInIt()
. So the necessary data will loaded when component is invoked or executed. (But I know this is not an efficient way)
loadEmployees() {
this.restApi.getEmployees().subscribe((data: {}) => {
this.Employee = data;
for(let emp of this.Employee){
this.restApi.getEmployeeAsset(emp.id).subscribe((data: {}) => {
this.assets = data;
if(this.assets.length !== 0){
for(let asset of this.assets){
this.reportData.push({"eid": emp.id,"employeeId":emp.employee_id,"firstName":emp.first_name,"lastName":emp.last_name,"email":emp.email,
"departmentId":emp.department_id,"designation":emp.designation,"eActive":emp.is_active,"invId":asset.id,"deviceName":asset.device_name,
"deviceTypeId":asset.device_type_id,"deviceTypeName":asset.device_type_name,"serialNo":asset.serial_no,"model":asset.model,"processor":asset.processor,
"ramId":asset.ram_id,"hddId":asset.hdd_id,"osId":asset.os_id,"warrantyDate":asset.warranty_date,"purchaseDate":asset.purchased_date,
"description":asset.description,"repair":asset.repair,"invActive":asset.is_active,"ramSize":asset.ram_size,"hddSize":asset.hdd_size,
"osName":asset.os_name,"assetStatus":asset.asset_status})
}
}
else {
this.reportData.push({"eid": emp.id,"employeeId":emp.employee_id,"firstName":emp.first_name,"lastName":emp.last_name,"email":emp.email,
"departmentId":emp.department_id,"designation":emp.designation,"eActive":emp.is_active})
}
})
}
})
}
I know this approach is not that efficient, because the reportData
array will be created at the backend irrespective of whether Download button is clicked or not. But Since I'm not getting how to handle this using Promises
or to handle Asynchronous coding, I ended up doing this.
Upvotes: 0
Views: 553
Reputation: 2107
You are mixing async code with synchronous code in a way that results in your data array not being populated with the results. The async code is still executing by the time this.reportData.map()
is being called. Need to consider using some of the rxjs operators on your API request.
download() {
...csv setup here
// Create an array of observables based on the Employee array
forkJoin(this.Employee.map(emp => this.restApi.getEmployeeAsset(emp.id))
// Pipe the Observables through the map operator to set up data
.pipe(
map(assets => {
assets.forEach(
if(assets.length !== 0){
this.reportData.push(...the data you want)
} else {
this.reportData.push(...the other data you want)
}
})
).subscribe(() => {
...do what you need here with mapping over this.reportData to create your csv
})
}
Upvotes: 1