0

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>

8
  • 1
    It's better if u shared more code, well there are some basic mistake, first, you are declaring info without this, but you are trying to access info with this in next line, this.info will create new info object in your context which I believe is some event handler, now if you try to access this.info when you remove the second you will get an error because there is not any info on this context, read tylermcginnis.com/this-keyword-call-apply-bind-javascript & developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… Commented Jul 24, 2019 at 7:28
  • @Harshkurra I can't declare info with this in typescript. Commented Jul 24, 2019 at 7:46
  • show where you are calling previewInfo(infos) Commented Jul 24, 2019 at 8:26
  • @LuisRico updated as per request. Commented Jul 24, 2019 at 8:32
  • better that you share a MVCE stackblitz here Commented Jul 24, 2019 at 8:55

2 Answers 2

1

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

Sign up to request clarification or add additional context in comments.

Comments

0

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

Comments

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.