0

I'm trying to solve this issue: I have an array of objects, each containing an image property, which is an absolute url to a remote image. I have to find the first object with an image larger than 500px, extract it from the array and putting it into another object.

What I've tried to do is to cycle through the array and call a function that loads the image and returns the width, but it doesn't work…

var app = angular.module('plunker', []);

app.controller('MainCtrl', function($scope, loadImages) {
  var articles = [
  {
    "image": "http://i.res.24o.it/images2010/logo.gif"
  },
  {
    "image": "http://www.avvenire.it/Commenti/PublishingImages/ImmaginiArticolo/Originali/gispponema_47548148.jpg"
  },
  {
    "image": "http://i.dailymail.co.uk/i/pix/2012/03/07/article-2111440-1211004C000005DC-146_1024x615_large.jpg"
  },
  {
    "image": "http://www.brookings.edu/~/media/research/images/w/wa%20we/wealthy002/wealthy002_16x9.jpg"
  },
  {
    "image": "http://www.avvenire.it/Mondo/PublishingImages/ImmaginiArticolo/Originali/oREUVENRIV_47517580.jpg"
  }
];

  $scope.articles = loadImages.getCover(articles);
})
.factory('loadImages', function(){
  
  function getMeta(url){
      var img = new Image();
      img.src = url;
      img.onload = function(){
          return img.width;
      };
  }
  return {
    getCover: function(articles) {
      var cover = null;
        for (var i = 0; i < articles.length; i++) {
            if(articles[i].image){
                var w = getMeta(articles[i].image);
                if(w > 500){
                  cover = articles[i];
                  articles.splice(i,0);
                }
            }
        }
        return {
          cover: cover,
          articles: articles
        };
    }
  };
});
<!DOCTYPE html>
<html ng-app="plunker">

  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <link rel="stylesheet" href="style.css" />
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

  </head>

  <body ng-controller="MainCtrl">
    <p>{{articles}}!</p>
  </body>

</html>

plunkr: http://plnkr.co/edit/tCacRy0jf9WhWreWIK7I

Do you have any suggestion? What am I doing wrong? Thanks

Camillo
  • 35
  • 8
  • What is getting returned for as w in the factory? I'm guessing the scope is binding with an undefined width BEFORE the images have loaded. Might be a good time to use $q promises or the async module so that the scope isn't bound until after the images have loaded. – James P Sep 10 '15 at 16:41

1 Answers1

0

Thanks to @JamesP comment I tried combining an asynchronous cycle found here (Asynchronous for cycle in JavaScript) and the $q promises in this way. I don't know if it's the best way to do it, but it works.

var app = angular.module('plunker', []);

app.controller('MainCtrl', function($scope, loadImages) {
  var articles = [
  {
    "image": "http://placehold.it/200"
  },
  {
    "image": "http://placehold.it/300"
  },
  {
    "image": "http://placehold.it/600"
  },
  {
    "image": "http://placehold.it/700"
  },
  {
    "image": "http://placehold.it/800"
  }
];
  loadImages.asyncLoop(articles.length, function(loop){
    var i = loop.iteration();
    loadImages.getMeta(articles[i].image).then(function(r){
     var cover = articles[i];
     articles.splice(i,1);
     $scope.articles = {
       cover: cover,
       articles: articles
     };
    }, function(){
      loop.next();
    })
  }, function(){
    $scope.articles = articles;
  });
})

.factory('loadImages', function($q){
  return {
    getMeta: function(url){
    var deferred = $q.defer();
    try{
      var img = new Image();
      img.src = url;
      img.onload = function(){
        if(img.width > 500){
          deferred.resolve(img.width);
        }else{
          deferred.reject();
        }
      }
    }catch(e){
      deferred.reject(e);
    }
  
    return deferred.promise;
  },
    asyncLoop: function(iterations, func, callback) {
      var index = 0;
      var done = false;
      var loop = {
          next: function() {
              if (done) {
                  return;
              }
  
              if (index < iterations) {
                  index++;
                  func(loop);
  
              } else {
                  done = true;
                  callback();
              }
          },
  
          iteration: function() {
              return index - 1;
          },
  
          break: function() {
              done = true;
              callback();
          }
      };
      loop.next();
      return loop;
  }
  };
});
Community
  • 1
  • 1
Camillo
  • 35
  • 8