Amareesh
Amareesh

Reputation: 368

Why am I getting empty Array outside of FOR loop in Typescript (Angular CLI)?

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

Answers (1)

Andrew Nolan
Andrew Nolan

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

Related Questions