如果ngSrc路径解析为404,是否有办法回退到默认值?


129

我正在构建的应用程序要求用户设置4条信息,然后才能加载该图像。该图像是应用程序的核心,因此断开的图像链接使整个外观看起来很闷。我想在404上放另一张图片。

有任何想法吗?我想避免为此编写自定义指令。

令我惊讶的是,我找不到类似的问题,尤其是当文档中的第一个问题相同时!

http://docs.angularjs.org/api/ng.directive:ngSrc



Answers:


252

这是一个非常简单的指令,用于监视加载图像并替换src时的错误。 (插棒)

HTML:

<img ng-src="smiley.png" err-src="http://google.com/favicon.ico" />

Javascript:

var app = angular.module("MyApp", []);

app.directive('errSrc', function() {
  return {
    link: function(scope, element, attrs) {
      element.bind('error', function() {
        if (attrs.src != attrs.errSrc) {
          attrs.$set('src', attrs.errSrc);
        }
      });
    }
  }
});

如果要在ngSrc为空白时显示错误图像,则可以添加以下内容(Plunker)

attrs.$observe('ngSrc', function(value) {
  if (!value && attrs.errSrc) {
    attrs.$set('src', attrs.errSrc);
  }
});

问题在于,如果该值为空白,则ngSrc不会更新src属性。


如果网址已损坏(404),效果很好,但是如果它是空字符串,则ng-src会默默地吞下该错误。
Stephen Patten 2013年

2
如果空字符串对您的模型无效,则进行建模,以使您使用ng-src绑定到的表达式不会返回空字符串。
贾斯汀·洛夫罗

更新了脚本以支持使用图像的src属性err-srcplnkr.co/edit/b05WtghBOHkxKdttZ8q5
Ryan Schumacher

@JustinLovero我认为这是一个有角度的错误并报告了它。问题在于,如果该值为空白,则ngSrc不会设置src属性。例如,如果您正在显示一部电话,并使用缺少图像URL的ajax加载另一部电话,那实际上可能是个问题。旧手机的图像会继续显示,但我认为错误或占位符会更合适。是的,您可以在模型中处理它,但是我认为留下旧图像的行为比显示错误更不正确。
杰森·古玛

@JasonGoemaat,我将如何用文本替换损坏的图像?
simeg 2015年

48

聚会晚了一点,尽管我想出了一个解决方案,以解决我正在构建的系统中几乎相同的问题。

不过,我的想法是img全局处理每个图像标签。

我不想给我HTML不必要的指令,例如err-src这里显示的指令。通常,特别是对于动态图像,您不会知道它是否丢失,直到为时已晚。对我而言,在图像丢失的机会之外添加额外的指令似乎有些过头。

相反,我扩展了现有img标签-实际上,这就是Angular指令的全部用途。

所以-这就是我的想法。

注意:这需要完整的JQuery库,而不仅仅是JQlite Angular附带,因为我们正在使用.error()

您可以在此Plunker上看到它的运行情况

该指令看起来非常像这样:

app.directive('img', function () {
    return {
        restrict: 'E',        
        link: function (scope, element, attrs) {     
            // show an image-missing image
            element.error(function () {
                var w = element.width();
                var h = element.height();
                // using 20 here because it seems even a missing image will have ~18px width 
                // after this error function has been called
                if (w <= 20) { w = 100; }
                if (h <= 20) { h = 100; }
                var url = 'http://placehold.it/' + w + 'x' + h + '/cccccc/ffffff&text=Oh No!';
                element.prop('src', url);
                element.css('border', 'double 3px #cccccc');
            });
        }
    }
});

当发生错误时(这是因为图像不存在或无法访问等),我们捕获并做出反应。您也可以尝试获取图像大小-如果它们首先出现在图像/样式上。如果没有,则将自己设置为默认值。

本示例使用placehold.it代替图像显示。

现在,每张图片,无论是否使用srcng-src是否已覆盖,以防万一都无法加载...


2
非常好的解决方案!让我们将此票选为最高!
Matthijs 2014年

1
我必须说这很漂亮。我实际上根本不希望显示任何图像,所以我使用了:element.css(“ display”,“ none”); 全部隐藏起来
庞培2015年

1
nice :),或者您也可以使用element.remove();将其从DOM中完全删除。:)
Darren Wainwright 2015年

效果很好,谢谢!
ecorvo

编辑了您的解决方案,因此在路径为空时也可以使用。stackoverflow.com/a/35884288/2014288
andrfas

21

为了扩展Jason解决方案以捕获加载错误或源字符串为空的两种情况,我们只需添加一个手表即可。

HTML:

<img ng-src="smiley.png" err-src="http://google.com/favicon.ico" />

Javascript:

var app = angular.module("MyApp", []);

app.directive('errSrc', function() {
  return {
    link: function(scope, element, attrs) {

      var watcher = scope.$watch(function() {
          return attrs['ngSrc'];
        }, function (value) {
          if (!value) {
            element.attr('src', attrs.errSrc);  
          }
      });

      element.bind('error', function() {
        element.attr('src', attrs.errSrc);
      });

      //unsubscribe on success
      element.bind('load', watcher);

    }
  }
});

2
不错的代码,但添加监视表中的内容ng-if(空的src字符串)可以轻松地解决问题
alonisser 2013年

好像您可以用自己的指令替换ngSrc,并设置错误时绑定以使用errSrc,而不是使用单独的errSrc指令(如果您要这样做)。您可以使用attrs.$observe('ngSrc', function(value) {...});,这是ngSrc在内部使用的而不是$ watch的方式
Jason Goemaat 2014年

整个空白问题是因为ngSrc如果值空白则不会更新src属性,因此,如果您有自己的指令替换ngSrc,则不需要空白检查。
杰森·古玛

1
@Pokono-您可以在错误函数内部检查当前的'src'属性是否与'errSrc'相同。
user2899845 2015年

1
@BiswasKhayargoli-添加了一个取消订阅的调用,因此在初始加载后不会有任何处理费用
user2899845

11
App.directive('checkImage', function ($q) {
    return {
        restrict: 'A',
        link: function (scope, element, attrs) {
            attrs.$observe('ngSrc', function (ngSrc) {
                var deferred = $q.defer();
                var image = new Image();
                image.onerror = function () {
                    deferred.resolve(false);
                    element.attr('src', BASE_URL + '/assets/images/default_photo.png'); // set default image
                };
                image.onload = function () {
                    deferred.resolve(true);
                };
                image.src = ngSrc;
                return deferred.promise;
            });
        }
    };
});

在HTML中:

<img class="img-circle" check-image ng-src="{{item.profileImg}}" />

我觉得这应该是可以接受的答案,因为它不使用jQuery并解决了问题
Gregra

10

如果image为404或image为null为空,则无需使用指令,您可以使用ng-src过滤器,例如:

<img ng-src="{{ p.image || 'img/no-image.png' }}" />

2
不,如果p.image在AJAX调用中返回404,它将视为“ true”,而不继续进行第二个img / no-image.png。
James Gentes

2
@JamesGentes-事实并非如此。ng-src的这种用法对我来说完全正确,如所示。而且非常简单。
shanemgrey 2015年

@shaneveeg,谢谢,这很有趣-我想知道后端是否有所不同。我在Express上运行Node,也许它处理方式有所不同。
James Gentes

2
@JamesGentes-更正我以前的评论。当模型返回有效的URI,但随后返回404时,OP会寻找解决方案。在这种情况下,此解决方案不起作用,会导致图像损坏。如果angular不为图像值返回任何值,则可以正确选择后备。
shanemgrey 2015年

我正在使用Laravel,如果是404(null或empty),它对我来说非常完美。但是Express可能会返回某种错误,而不是像404这样的代码响应,所以是的,可能取决于服务器框架
NeoNe

8

我使用类似的方法,但是它假定team.logo是有效的。如果未设置“ team.logo”或为空,则强制使用默认值。

<img ng-if="team.logo" ng-src="https://api.example.com/images/{{team.logo}}">
<img ng-hide="team.logo" ng-src="img/default.png">

2
这是正确的,因为它的计算结果“team.logo”作为一个字符串(真/假),而NG-SRC会看到{{team.logo}}为真,即使它返回一个404
詹姆斯氏族


1

您是否有特定原因无法在代码中声明后备图片?

据我了解,您的图片来源有两种可能的情况:

  1. 正确设置信息<4 =后备图像。
  2. 正确设置信息== 4 =生成的URL。

我认为这应该由您的应用处理-如果当前无法确定正确的URL,请传递加载/后备/占位符图像URL。

原因是您永远不会有“丢失”的图像,因为您已经明确声明了可以在任何时间显示的正确URL。


抱歉,我应该澄清一下,即使设置了所有4个值,URL仍然可以404(用户可以在此过程中添加文件夹。)与此同时,我添加了一个函数,当所有值都被设置时检查URL的响应头被设置。如果没有特殊处理,仍然可以解决问题,我敢打赌,我会再遇到这个问题:)
will_hardin

1
太棒了,您应该将其显示为答案,并将其标记为以后搜索的任何人都接受。
亚历克斯·奥斯本

1

我建议您可能希望使用Angular UI Utils的“ if语句”指令来解决您的问题,如http://angular-ui.github.io/所示。我只是用它来做完全一样的事情。

这未经测试,但是您可以执行以下操作:

控制器代码:

$scope.showImage = function () {
    if (value1 && value2 && value3 && value4) { 
        return true;
    } else {
        return false;
    }
};

(或更简单)

$scope.showImage = function () {
    return value1 && value2 && value3 && value4;
};

视图中的HTML: <img ui-if="showImage()" ng-src="images/{{data.value}}.jpg" />

甚至更简单,您可以只使用scope属性:

控制器代码:

$scope.showImage = value1 && value2 && value3 && value4;

视图中的HTML: <img ui-if="showImage" ng-src="images/{{data.value}}.jpg" />

对于占位符图像,只需添加另一个类似的<img>标记,但在ui-if参数前加上一个感叹号(!),然后使ngSrc拥有占位符图像的路径,或者仅src按照常规HTML 使用标记。

例如。 <img ui-if="!showImage" src="images/placeholder.jpg" />

显然,以上所有代码示例均假定当您的4条信息中的每条信息不完整时,value1,value2,value3和value4中的每一个都等于null/ false(因此,true当它们完成时,它们也都等于一个布尔值)。

PS。AngularUI项目最近被细分为子项目,并且ui-if目前似乎缺少的文档(不过可能在软件包中的某个位置)。但是,您可以看到它非常简单易用,并且我已经在Angular UI项目上记录了Github“问题”,并向团队指出了这一点。

更新:AngularUI项目中缺少“ ui-if”,因为它已集成到核心AngularJS代码中!但是仅从v1.1.x版本开始,当前标记为“不稳定”。


ng-if使其成为核心btw(如果我没记错的话,从1.1.5开始)。
全息原理

1

这是我使用本机javascript提出的解决方案。我正在检查图像是否损坏,然后在图像中添加一个类以防万一并更改源。

我从Quora答案中获得了部分答案,网址为http://www.quora.com/How-do-I-use-JavaScript-to-find-if-an-image-is-broken

app.directive('imageErrorDirective', function () {
    return {
        restrict: 'A',
        link: function (scope, element, attrs) {
            element[0].onerror = function () {
                element[0].className = element[0].className + " image-error";
                element[0].src = 'http://img3.wikia.nocookie.net/__cb20140329055736/pokemon/images/c/c9/702Dedenne.png';
            };
        }
    }
});

0

想出了自己的解决方案。如果src或ngSrc为空,并且img返回404,它将替换图像。

(@Darren解决方案的分支)

directive('img', function () {
return {
    restrict: 'E',        
    link: function (scope, element, attrs) {   
        if((('ngSrc' in attrs) && typeof(attrs['ngSrc'])==='undefined') || (('src' in attrs) && typeof(attrs['src'])==='undefined')) {
            (function () {
                replaceImg();
            })();
        };
        element.error(function () {
            replaceImg();
        });

        function replaceImg() {
            var w = element.width();
            var h = element.height();
            // using 20 here because it seems even a missing image will have ~18px width 
            // after this error function has been called
            if (w <= 20) { w = 100; }
            if (h <= 20) { h = 100; }
            var url = 'http://placehold.it/' + w + 'x' + h + '/cccccc/ffffff&text=No image';
            element.prop('src', url);
        }
    }
}
});

0

这将只允许循环两次,以检查ng-src是否不存在,否则请使用err-src,这样可以防止继续循环。

(function () {
    'use strict';
    angular.module('pilierApp').directive('errSrc', errSrc);

    function errSrc() {
        return {
            link: function(scope, element, attrs) {
             element.error(function () {
                // to prevent looping error check if src == ngSrc
                if (element.prop('src')==attrs.ngSrc){
                     //stop loop here
                     element.prop('src', attrs.errSrc);
                }              
            });
            }
        }
    }
})();
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.