Search code examples
angularjsangularjs-filteronerror

ng-src and onerror : Occasionally hiding a valid image


In order to show a default image (instead of a broken image) when the image is not physically present, I use this piece of code:

<img ng-show="order.img" ng-src="{{order.img | fPath}}" onerror="this.style.display='none';"/>
<img ng-hide="order.img" ng-src="{{'default.png' | imgPath}}" />

Here fPath,imgPath are filters that append a prefix to the image name.

Most of the times it works well (the image gets rendered). However, in some cases the image does not show up. In such cases, the angular snippet gets translated to this html (note the style="display: none;" in the first img tag):

<img ng-show="order.img" ng-src="http://xx.s3.amazonaws.com/o/img1.jpg" onerror="this.style.display='none'" src="http://xx.s3.amazonaws.com/o/img1.jpg" style="display: none;">
<img ng-hide="order.img" ng-src="http://xx.s3.amazonaws.com/default.png" class="ng-hide" src="http://xx.s3.amazonaws.com/default.png" style="">

Question: What could be the reason for this? And possible workarounds? Is it that the onerror gets triggered sooner than expected?

PS: http://xx.s3.amazonaws.com/o/img1.jpg is a valid image path and is available.

Update: This is how the app/route/controller is defined

Route

t_app.config(function ($stateProvider, $urlRouterProvider, $httpProvider) {
    // ...
    $stateProvider.state('ref', {
        url: '/orders/:ref',
        templateUrl: 'views/order.htm',
        controller: 'orderController'
    });
    // ...
});

Controller

t_app.controller('orderController', ['$scope', '$stateParams', function($scope, $stateParams) {
      // Load the order (this step takes a while)
}]);

Solution

  • I think what is happening to you is this: http://plnkr.co/edit/8JL0Op?p=preview

    angular.module('app.helper', [])
    .controller('ImgController', ['$scope', '$timeout', function($scope, $timeout) {
      $scope.img = "";
      $timeout(function(){
        $scope.img = "9c5595db-db21-4783-902a-8e80b84ae22c/6b89ed2b-878a-4f76-95c4-76ee95f47d9a.jpg";
      }, 1500);
    }]);
        filter.$inject = ["$filter"];
        angular.module('app.helper').filter('addPath', filter);
    
        function filter($filter) {
            function filterFn(input) {
                return "http://cdn.playbuzz.com/cdn/"+input;
            }
    
            return filterFn;
        }
    

    The filter adds the prefix before order.img is set, so the img src will be (until order is lodaded) the prefix only, without the image name, resulting in a wrong src. I think, but i can't be 100% sure, the reason because it happens only sometimes is that sometime $scope.order is loaded before (maybe when the results are cached or something) onerror triggers and some times after.
    It's hard to determine the execution order when you mix native javascript and angular

    You can probably avoid this by adding a check in your filter: if the value passed to the filter is null, undefined or an empty string then return null in that case. Something like this

    return typeof input === "undefined" || input === null || input === "" ? null : "http://cdn.playbuzz.com/cdn/"+input;