active92
active92

Reputation: 654

Angular: Declaring array of objects in Typescript

I'm trying to declare an array of object in Typescript. However, I'm facing error retrieving the object. Below is my code. Image shows output of this.attachments.

info: Info[];

if (this.attachments.length > 0) {
  this.previewInfo(this.attachments);
}

previewInfo(infos) {
  this.info = [];
  for (let i = 0; i < infos.length; i++) {
    let reader = new FileReader();
    reader.onload = (e: any) => {
      var temp = new Info;
      temp = {
        id: i,
        url: e.target.result,
        message: ""
      }
      this.info.push(temp);
    }
    reader.readAsDataURL(infos[i]);
  }
}

The result I get contains an empty array in front which looks like this.

[]0: {id: 0, url: "test1", message: ""}
1: {id: 1, url: "test2", message: ""}
2: {id: 2, url: "test3", message: ""}

This causes undefined when I try to retrieve them using

this.info[0]

If I skip the second line which is this.info=[], I'm getting an error which says

Cannot read property '0' of undefined

Did I declare it wrongly? How can I retrieve info by index?

Image shows output of <code>this.attachments</code>

Upvotes: 0

Views: 8088

Answers (2)

AVJT82
AVJT82

Reputation: 73347

Akber Iqbal's answer works, but their solution does not guarantee that the files are in the same order as the attachments array, so

here is a solution, if there is an importance of that the info items are in the same order as the files in the attachments array. You are looking for the first item, so maybe it is important. We can use Promises and async/await:

async onFileSelected(event) {
  this.attachments = [];
  this.info = [];

  for (let index = 0; index < event.target.files.length; index++) {
    let file = event.target.files[index];
    this.attachments.push(file);
  }

  if (this.attachments.length > 0) {
    for (let i = 0; i < this.attachments.length; i++) {
      try {
        // wait that the file has been processed before moving on to next file
        let temp = await this.readFile(this.attachments[i], i);
        this.info.push(temp)

        if (this.attachments.length === this.info.length) {
          // here we have the first item after all files has been completed
          console.log(this.info[0])
        }
      } catch (err) {
        console.log(err);
      }
    }
  }
}

And processing the file from the for-loop here, by passing the file and the index and returning the temp file:

readFile(file, i) {
  return new Promise((resolve, reject) => {
    let reader = new FileReader();

    reader.onload = (e: any) => {
      let temp = {
        id: i,
        url: e.target.result,
        message: file.name, // for testing I put the file name as message
      }
      resolve(temp);
    };
    reader.readAsDataURL(file)
  })
}

DEMO: StackBlitz

Upvotes: 1

Akber Iqbal
Akber Iqbal

Reputation: 15031

The issue is of asynchronous calls;

When your loop finishes and these 2 lines are executed... by that time the reader.onload hadn't finished and therefore this.info.push(temp); didn't run and you see a blank in your console when you run these 2 lines below:

console.log(this.info);
console.log(this.info[0]); //this is what i need

What we need to do is to let the loop finish, and in the very last iteration (where i == infos.length - 1) of the loop... we print the values on the console and get the correct result;

relevant TS:

import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})

export class AppComponent {

  //Variable declaration
  attachments = [];
  info = [];

  onFileSelected(event) {
    this.attachments = [];
    for (var index = 0; index < event.target.files.length; index++) {
      var file = event.target.files[index];
      this.attachments.push(file);
    }
    if (this.attachments.length > 0) {
      this.previewInfo(this.attachments);
      console.log('caller', this.attachments);
    }
  }

  previewInfo(infos) {
    this.info = [];
    if (infos) {
      for (let i = 0; i < infos.length; i++) {
        let reader = new FileReader();
        reader.onload = (e: any) => {
          let temp = {
            id: i,
            url: e.target.result,
            message: "",
          }
          //console.log(temp);
          if (i == infos.length - 1) {
            this.pusher(temp, true);
          } else {
            this.pusher(temp, false);
          }
          //this.info.push(temp, function(){ console.log('pushed'); } );
        }
        reader.readAsDataURL(infos[i]);
      }
      console.log('empty: ', this.info);
      console.log('empty: ', this.info[0]); //this is what i need
    }
  }

  pusher(tempFile, lastFile) {
    this.info.push(tempFile);
    if (lastFile == true) {
      console.log('Correct Filled: ', this.info.length);
      console.log('Correct Filled: ', this.info[0]); //this is what i need
    }
  }

}

complete working stackblitz here

Upvotes: 0

Related Questions