3

I'd like to check an image to see if the resource is available, before displaying it. I found a good way to do that in AngularJS here: Angular js - isImage( ) - check if it's image by url

But every time I try to implement it, an infinite loop is triggered, even if I reduce the function to its simplest form in a codepen : https://codepen.io/anon/pen/mBwgbE

test image function (js)

$scope.testImage = function(src) {
    console.log('function triggered');
                Utils.isImage(src).then(function(result) {
             return "result";
         });
    };

Usage (html)

<h3>Image link broken<h3>
<p>{{testImage('anylink')}}</p>

<h3>Image link OK<h3>
<p>{{testImage('http://lorempixel.com/400/200/sports/1/')}}</p>

Can anyone explain this behaviour to me, and help me fix it?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Leon
  • 1,851
  • 3
  • 21
  • 44

2 Answers2

3

Angular runs the digest loop, and interprets your template. It sees {{testImage('anylink')}} and calls into it. This calls into Utils.isImage, which creates a promise. The promise is returned to testImage, but testImage itself doens't return anything, so the template shows nothing.

A little later, the promise resolves. Angular sees this, so it runs the digest loop and interprets your template. It sees {{testImage('anylink')}} and calls into it. This calls into Utils.isImages, which creates a prom... oh crap, we're in an loop. It's going to call isImage, which creates a promise, and then when that promise resolves, it interprets the template again and calls isImage, starting all over.

Instead, i would recommend that when your controller loads, you create the promises right then, and when they resolve, you stick whatever values you need from them onto the controller as concrete values. Something like this:

function myController($scope, Utils) {
    $scope.anyLink = null;
    $scope.sportsLink = null;
    Utils.isImage('anyLink')
        .then(function (result) { $scope.anyLink = result });
    Utils.isImage('http://lorempixel.com/400/200/sports/1/')
        .then(function (result) { $scope.sportsLink = result });

    $scope.heading = "My Controller";
}

And then on your template, interact with $scope.anyLink or $scope.sportsLink

Nicholas Tower
  • 72,740
  • 7
  • 86
  • 98
  • Will this work dynamically? There could be hundreds of images to check, reading from a database – Leon Sep 30 '17 at 11:06
  • Thank you for your explanation – Leon Sep 30 '17 at 11:07
  • 1
    You should be able make a database request and then when it completes loop through the results, saving the results onto some array. You'll just want to make sure to do this once, when the controller is created, not every time the template is interpolated. – Nicholas Tower Sep 30 '17 at 11:09
0

$scope.testImage is automatically watched by angular to see change of testImage. As a result you can stop infinite loop by using $scope.cache variable.

  $scope.testImage = function(src) {
    console.log('function triggered');
    if($scope.cache[src] == "result")
      return "result";
    Utils.isImage(src).then(function(result) {
             $scope.cache[src] = "result";
             return "result";
         });
    }; 

It was tested on your codepen.

artgb
  • 3,177
  • 6
  • 19
  • 36
  • This particular bit of code seems to have syntax errors... also, as $scope.cache is not set to "result", the function never runs, and it will never BE set. – Leon Oct 07 '17 at 08:57