2

I'm trying to load some images using node's fs module. I have it working on a single image but what is the correct way to do this inside an ngFor loop?

Currently I have:

<div *ngFor="let job of getJobs() | async">
   <img src={{readImageFile(job.thumbUrl)}}>
</div>

getJobs() returns an observable from my service.

readImageFile() calls a Meteor server-side method which loads the image data using fs and returns it asynchronously:

  readImageFile(url) {
    if (url) {
      this.call('readImageFile', url, function(err, res) {
        if (err) {
          console.log(err);
        }
        else {
          console.log('read image file success');
          return "data:image/jpg;base64," + res;
        }
      });
    }
  }

This doesn't work.. So what is the correct method for asynchronously loading data inside an ngFor loop?

update

I tried with readImageFile(url) | async

readImageFile: function(url) { 
  var Future = Npm.require('fibers/future'); 
  var myFuture = new Future(); 
  fs.readFile(String(url), 
  function read(error, result) { 
    if(error){ 
      myFuture.throw(error); 
    } else { 
      myFuture.return(new Buffer(result).toString('base64')); 
    } 
  }); 
  return myFuture.wait(); 
}

1 Answer 1

2

This is not a good approach. readImageFile(job.thumbUrl) will be called with each change detection run which is quite often.

This should work

<img [src]="readImageFile(job.thumbUrl) | async">

The | async pipe should prevent change detection calling readImageFile() repeatedly.

Another approach would be to fill an array and bind to this array instead

getJobs() {
  this.jobs = /*code to get jobs*/.map(jobs => {
    this.images = new Array(jobs.length);
    jobs.forEach(item, idx => { 
      readImageFile(idx, item.thumbUrl);
    });
    return jobs;
  });
}
  readImageFile(idx, url) {
    if (url) {
      this.call('readImageFile', url, (err, res) => {
        if (err) {
          console.log(err);
        }
        else {
          console.log('read image file success');
          this.images[idx] = "data:image/jpg;base64," + res;
        }
      });
    }
  }
<div *ngFor="let job of jobs | async; let idx=index">
   <img [src]="images[idx]">
</div>
11
  • Hi, thanks for your reply, I tried your first suggestion and its still being called repeatedly.. Any idea why?
    – Michael B
    Commented Nov 6, 2016 at 15:39
  • Does readImageFile() return an Observable? If yes, does the observable complete after the first event? Commented Nov 6, 2016 at 15:54
  • This is the readImageFile meteor method on the server. How can i find out what type it is and whether it completes? thanks
    – Michael B
    Commented Nov 6, 2016 at 16:13
  • readImageFile: function(url) { var Future = Npm.require('fibers/future'); var myFuture = new Future(); fs.readFile(String(url), function read(error, result) { if(error){ myFuture.throw(error); }else{ myFuture.return(new Buffer(result).toString('base64')); } }); return myFuture.wait(); },
    – Michael B
    Commented Nov 6, 2016 at 16:14
  • 1
    I finally got this working! Its exactly the same as your second approach but inside the map function I had to add "return jobs;" at the end to get it to work. Thank you so much for your help! In my case i have two jobs, but the readImageFile callback seem to be logging to the console three times. Do you know why? Does that mean i'm reading the image more times than is necessary?
    – Michael B
    Commented Nov 7, 2016 at 12:57

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.