48

I am using the ng-src directive in order to avoid the browser to request the image before Angular evaluate the expression.

ng-src={{image}} will update the src attribute of the image if the expression "image" change. I misunderstand why the ng-src directive doesn't update the path of the image if the expression ("myImage.png") become empty ("").

When the expression become empty, the ng-src attribute become empty but the src attribute still the last known src. So it doesn't update the image. Why the src attribute isn't updated (to an empty src) so that the image "disappear".

Here is a plunker

Thanks

gab
  • 4,073
  • 3
  • 17
  • 16

9 Answers9

41

The answer to this is in the Angular code. It's not a bug, it's just the behavior they decided they wanted. Starting on line 13895, you can see this directive code:

forEach(['src', 'srcset', 'href'], function(attrName) {
   var normalized = directiveNormalize('ng-' + attrName);
   ngAttributeAliasDirectives[normalized] = function() {
   return {
      priority: 99, // it needs to run after the attributes are interpolated
      link: function(scope, element, attr) {
          attr.$observe(normalized, function(value) {

         if (!value)
             return;

         attr.$set(attrName, value);

         if (msie) element.prop(attrName, attr[attrName]);
       });
     }
    };
  };
});

The key is in:

if (!value) return;

So as you can see, if you change the ng-src expression to an empty string, this will never change the actual src value. You can get around this doing something MadScone suggested.

T.M.
  • 3,111
  • 1
  • 18
  • 15
  • 3
    The question is more, why did they choose such a behavior. It leaves the image element in an inconsistant state. The image is shown while the model is empty. – gab Feb 28 '14 at 15:02
  • Hmm, I'm not sure what were their thought behind this decision. What I think is: – T.M. Feb 28 '14 at 22:50
  • Hmm, I'm not sure what were their thoughts behind this decision. What I think is: As per [link](http://dev.w3.org/html5/markup/img.html), **src** has to be a _non-empty URL potentially surrounded by space_ in order to be valid. But more important than that, as you can read here [link](http://gtmetrix.com/avoid-empty-src-or-href.html), an empty **src** can lead to multiple problems. Maybe this was what they were trying to prevent. – T.M. Feb 28 '14 at 22:57
  • Ok, thank you for these links! I didn't know much about an empty src. Anyway @MadScone trick is a great workaround! It is tempting to add this trick in a custom directive (data-custom-src) :) – gab Mar 01 '14 at 09:09
  • You can also use jQuery to set the image src directly. Not sure how big of an impact it'll have on the subsequent jQuery digest cycles. – Edmond Chui Feb 13 '19 at 17:40
40

MadScone's suggestion is a cool idea, but it didn't work for me in all browsers. I ended up just showing the element only when the src isn't empty:

<img ng-show="theImage!==''" ng-src="{{theImage}}">

This seems like the safest option to me after reading through the thread that MadScone referenced (here). As a number of people pointed out there, the accepted answer doesn't work in all browsers and has some other issues that could arise. Simply hiding the element avoids all that.

Update: As enpenax pointed out in the comments, the !=='' in the ng-show is totally unnecessary. Here's the cleaner version:

<img ng-show="theImage" ng-src="{{theImage}}">
developering
  • 1,325
  • 15
  • 16
  • 6
    for me this does the trick already as ng-show evaluates to bool and an empty string AND null will turn out to be false. – enpenax Mar 13 '15 at 02:14
  • I also came to the same conclusion that using the ng-show is the better work around to the problem. I actually use ng-if, but the concept of the solution is the same. – Alappin Jun 11 '15 at 13:06
  • This is way late, but @enpenax has a great point. Empty string is falsy, so the !== was totally unnecessary. I'll update with that info. – developering Jun 18 '15 at 23:38
27

Update (April 2015)

See developering's answer, apparently this method I suggested originally may not work in all browsers.


Original (February 2014)

I'm not fully sure why that's happening, but here's a way of solving it anyway. Add this function to your controller:

$scope.getImage = function(src) {
  if (src !== "") {
    return src;  
  } else {
   return "//:0"; 
  }
};

The "blank" image will get a source of //:0 which won't cause a missing image icon to appear (see the top answer on this thread).

Then you can set your source using:

<img ng-src="{{getImage(superImage)}}" />

EDIT:

Actually, it would be easier to change your button click to this:

<button ng-click="superImage = '//:0'">Remove superImage</button>

Then there's no need for the function. Although I think the function method is better.

Community
  • 1
  • 1
Ciarán Tobin
  • 7,306
  • 1
  • 29
  • 45
  • 2
    shorthand: `return src || "//:0";` – roberkules Apr 21 '14 at 14:01
  • This is a cool idea and worked for me in some browsers, but not in everything. The main issue is that "//:0" doesn't actually work everywhere. I added another answer that will should across the board. – developering Mar 02 '15 at 19:52
  • 1
    @developering Fair enough. I've updated my answer for any unsuspecting people in future who may not read the all the answers or might miss your comment. – Ciarán Tobin Apr 13 '15 at 11:24
9

Another short way of solving it: <img ng-src="{{image?image:' '}}" />

cfuglesang
  • 91
  • 1
  • 2
4

fix it by set value = " "; with whitespace.

I also add the following css to make sure it's not displayed.

img[src="_"]
    display none !important

the real reason has been discussed here: https://github.com/angular/angular.js/issues/1218

luochen1990
  • 3,689
  • 1
  • 22
  • 37
1

ng-hide and ng-show can help you solve it

<img ng-src="{{Image}}" ng-hide="Image == null" ng-show="Image != null" ng-click="ChooseImage()" width="100%"  />

$scope.Image = null;

or

<img ng-src="{{Image}}" ng-hide="Image === ''" ng-show="Image !== ''" ng-click="ChooseImage()" width="100%"  />

$scope.Image = '';

0

<img data-ng-src="{{image || 'http://www.1x1px.me/FFFFFF-0.png'}}" data-ng-model="image">

You can choose any 1x1px image that suitable to your website and replace http://www.1x1px.me/FFFFFF-0.png of your own. For my website I use available image on http://www.1x1px.me.

P/S: No need to worry about the original src attribute of img tag because it's not affected to image model

0

A possible workaround to empty ng-src can be found here.

<img ng-src="{{image || '//:0' }}" />

Detailed explanation to use //:0 can be found from this answer.

To summarize:

It adopts the current protocol, omits the hostname and sets the port to zero, which is invalid and should be killed by the network layer.

Also instead of emptying ng-src, if you have a default image source then you can simply set the ng-src to that default src when ng-src have to be empty.

-3
 <img src="{{superImage}}" />   

This works as expected Edit:

Other work arround,may be you could have used and empty string.

ng-click="superImage = ' '  

to make it empty. It does not trigger 404

Jayantha Lal Sirisena
  • 21,216
  • 11
  • 71
  • 92
  • using the src attribute will trigger a request before Angular evaluate the expression superImage which will result in a 404 not found. – gab Feb 28 '14 at 10:51