14

I'm trying to set an image source using data returned from a modal. This is inside an ng-repeat loop:

<div id="divNetworkGrid">
    <div id="{{network.id}}" ng-repeat="network in networks">
        <span>
            <table>
                <tr>
                    <td class="imgContainer">
                        <img ng-src="{{ ('assets/img/networkicons/'+ network.actual + '.png') || 
                                         'assets/img/networkicons/default.png' }}"/>
                    </td>
                </tr>
            </table>
        </span>
    </div>
</div>

As is apparent, I want to populate default.png when the network.actual model property is returned as null. But my code is not picking up the default image, though the first image is coming up fine when available.

I'm sure this is some syntax issue, but cant figure out what's wrong.

1
  • I have often wondered how to do this and have tried such things as you have, but it had never seemed to work! Good question! Commented Sep 17, 2015 at 2:27

6 Answers 6

31

I have just found out, that you can use both ng-src and src on an img tag and if ng-src fails (variable used does not exists etc.), it will automatically fallback to src attribute.

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

1 Comment

I thinks solution is better if you do not want to write more code.
19

What I imagine is happening is the src is returning a 404 even if network.actual is null. For example, first value:

('assets/img/networkicons/'+network.actual+'.png')

is evaluating to a 404 src URL, something like ('assets/img/networkicons/null.png'). So in the OR selection, it's truth-y, but the resource is 404. In that case, you can set the fallback via onError with something like:

<img ng-src="{{('assets/img/networkicons/'+network.actual+'.png')}}" onerror="this.src='assets/img/networkicons/default.png'" />

CodePen to demo use cases - http://codepen.io/jpost-vlocity/pen/zqqerL

3 Comments

This is perfect if the +network.actual+ var exists but the image does not!
The best solution as to me
Clean, simple and neat. Works well.
15
angular.module('fallback',[]).directive('fallbackSrc', function () {
    return{
        link: function postLink(scope, element, attrs) {
            element.bind('error', function () {
                angular.element(this).attr("src", attrs.fallbackSrc);
            });
        }
    }
});

<img ng-src="{{url}}" fallback-src="default-image.jpg"/>

angular.module('fallback', []).directive('actualSrc', function () {
    return{
        link: function postLink(scope, element, attrs) {
            attrs.$observe('actualSrc', function(newVal, oldVal){
                 if(newVal != undefined){
                     var img = new Image();
                     img.src = attrs.actualSrc;
                     angular.element(img).bind('load', function () {
                         element.attr("src", attrs.actualSrc);
                     });
                 }
            });

        }
    }
});

<img actual-src="{{url}}" ng-src="default.jpg"/>

link

3 Comments

I was reluctant to try this initially because I thought it must be a simpler solution. However, after I tried many suggestions and none of them work perfectly, this is definitely the best one. Thanks!
@miliu Glad it helped you :)
minor error: first example should use fallback-src as the attribute html code (or fallBackSrc as the directive name). Great solution otherwise!
10

Interesting way to use ng-src. I haven't tested how that expression would be handled but I would suggest doing it a more stable way:

<img ng-src="{{ networkIcon }}" />

And in your controller:

$scope.networkIcon = network.actual ? 'assets/img/networkicons/' + network.actual + '.png' : 'assets/img/networkicons/default.png';

Edit: Just to clarify, I'm not suggesting the best way to do this is to bind the image source directly to the scope but rather to move the image fallback logic outside of the view. In my applications I often do it in the services that retrieve the data or on the server itself and send an image URL so the client only has to worry about a single property.

Edit to address repeater:

angular.forEach($scope.networks, function(network) {
  network.icon = network.actual ? 'assets/img/networkicons/' + network.actual + '.png' : 'assets/img/networkicons/default.png';
});

<tr ng-repeat="network in networks">
  <td><img ng-src="{{ network.icon }}" /></td>
</tr>

3 Comments

I should have specified before, but network.actual is actually within an ng-repeat. network is an iteration of the networks model. So this option wouldn't work for me, unfortunately...
Yes it will, in my edit paragraph I mentioned I do it in services that retrieve the data. So before you bind to the repeater iterate through your data and replace (or append) the image source.
I've updated my answer with an example of how to modify the data before it hits the repeater.
8
<div id="divNetworkGrid">
                <div id="{{network.id}}" ng-repeat="network in networks">
                    <span>
                        <table>
                            <tr>
                                <td class="imgContainer">
                                    <img ng-src="{{'assets/img/networkicons/'+(network.actual || 'default' )+'.png'}}"/>
                                </td>
                            </tr>
                        </table>
                    </span>
                </div>
            </div>

The inline or will work if the the queried variable is undefined. The above will fix your issue

Comments

5

You can supply the alternative image after OR operator like this:

{{ book.images.url || 'Images/default.gif' }}

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.