ngSrcパスが404に解決される場合、デフォルトにフォールバックする方法はありますか?


129

私が作成しているアプリケーションでは、このイメージがロードされる可能性がある前に、ユーザーが4つの情報を設定する必要があります。この画像はアプリケーションの中心部分であるため、壊れた画像リンクにより、全体が壊れているように見えます。別の画像を404に置きたいのですが。

何か案は?このためのカスタムディレクティブを作成しないようにしたいと思います。

特にドキュメントの最初の質問が同じである場合、特に同様の質問が見つからないことに驚きました。

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


これは関連があるかもしれません:stackoverflow.com/q/13781685/1218080
holographic-principle

回答:


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属性を更新しないことです。


URLが壊れている(404)場合は正常に機能しますが、空の文字列の場合、ng-srcはエラーを通知せずに飲み込みます。
スティーブンパッテン2013

2
空の文字列がモデルで有効でない場合は、ng-srcでバインドしている式が空の文字列を返さないようにしてください。
Justin Lovero、2014

画像のsrc属性の使用をサポートするようにスクリプトを更新しましたerr-srcplnkr.co/edit/b05WtghBOHkxKdttZ8q5
Ryan Schumacher

@JustinLovero私はangularのバグだと考え、報告しました。問題は、値が空白の場合、ngSrcがsrc属性を設定しないことです。たとえば、電話を表示していて、画像のURLが欠落しているajaxを含む別の電話をロードする場合、これは実際に問題になる可能性があります。古い電話の画像は引き続き表示されますが、エラーまたはプレースホルダーの方が適切だと思います。はい、モデルで処理できますが、古い画像を残す動作は、エラーを表示するよりも間違っていると思います。
Jason Goemaat 2014年

@JasonGoemaat、壊れた画像をテキストに置き換えるにはどうすればよいですか?
simeg

48

私が構築しているシステムの多かれ少なかれ同じ問題の解決策を思いついたが、パーティーに少し遅れた。

しかし、私の考えは、すべてのイメージimgタグをグローバルに処理することでした。

ここに示さHTMLれてerr-srcいるような不要なディレクティブを使用する必要はありませんでした。多くの場合、特に動的な画像では、手遅れになるまで欠落しているかどうかはわかりません。画像が欠落しているオフチャンスに追加のディレクティブを追加することは、私にはやりすぎに思えます。

代わりに、私は既存のimgタグを拡張します-これは実際、Angularディレクティブのすべてです。

だから-これは私が思いついたものです。

:これを使用するには、JQlite Angularだけでなく、完全なJQueryライブラリが必要です。.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を使用しています。

何もロードしない場合に備えて、srcまたはng-srcそれ自体がカバーされているかどうかに関係なく、すべての画像...


2
非常に素晴らしい解決策!これを上に投票しましょう!
Matthijs 2014年

1
これは私が言わなければならないかなり美しいです。解決しない場合、実際に画像を表示したくないので、次のように使用しました:element.css( "display"、 "none"); それを完全に隠すために
ポンペイ

1
nice :)またはelement.remove();を使用して、DOMから完全に削除することができます。:)
ダレンウェインライト、

ありがとうございます。
ecorvo 2016年

ソリューションを少し編集したため、パスが空の場合にも機能します。stackoverflow.com/a/35884288/2014288
andrfas 2016年

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

ngSrcを独自のディレクティブに置き換えて、エラーが発生した場合に別のerrSrcディレクティブを使用するのではなく、errSrcを使用するようにバインドさせることができるようです。attrs.$observe('ngSrc', function(value) {...});$ watchの代わりにngSrcが内部的に使用するものを使用できます
Jason Goemaat

空白に関する問題全体は、値が空白の場合、ngSrcはsrc属性を更新しないためです。そのため、ngSrcを置き換える独自のディレクティブがある場合、空白のチェックは必要ありません。
Jason Goemaat 14

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を使用せず、問題を解決するので、これは受け入れられる答えであると
思い

10

画像が404または画像がnullの場合、ディレクティブの必要がない場合は、次のように単純にng-srcフィルターを使用できます。

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

2
いいえ、p.imageがAJAX呼び出しで404を返す場合、それは「true」として扱われ、2番目のimg / no-image.pngに移動しません。
James Gentes、2015

2
@JamesGentes-そうではないようです。このng-srcの使用は、表示されているとおりに機能します。そして、それはとても簡単です。
shanemgrey 2015

@shaneveegありがとう、それは面白いです-バックエンドが何が違うのかしら。NodeをExpressで実行していますが、処理方法が異なる可能性があります。
James Gentes

2
@JamesGentes-以前のコメントの修正。OPは、モデルから返された有効なURIがある場合の解決策を探していますが、追跡された場合は404を探しています。その場合、このソリューションは機能せず、画像が壊れます。angularが画像の値に対して何も返さない場合、これはフォールバックを正しく選択します。
shanemgrey 2015

私はLaravelを使用していて、404、null、または空の場合は完璧に機能します。しかし、おそらく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
NG-srcが、それが404返す場合でも真と{{team.logo}}表示され、一方、それが文字列(真/偽の)として「team.logo」を評価し、これは、正しい
ジェームズgensの複数形


1

コードでフォールバックイメージを宣言できない特定の理由はありますか?

私が理解しているように、画像ソースには2つのケースが考えられます。

  1. 情報を正しく設定<4 =フォールバック画像。
  2. 情報を正しく設定== 4 =生成されたURL。

これはアプリで処理する必要があると思います。現在正しいURLを特定できない場合は、代わりにloading / fallback / placeholder画像URLを渡します。

その理由は、いつでも表示する正しいURLを明示的に宣言しているため、「欠落」したイメージが決してないということです。


申し訳ありませんが、4つの値がすべて設定されている場合でも、URLは404である可能性があることを明確にしたはずです(ユーザーは途中でフォルダを追加できます)。その間、すべての値が設定されています。特別な処理なしでこれを処理することはそれでもいいでしょう、私はこれに再び出くわすと思います:)
will_hardin

1
クールです。それを回答として示し、今後検索する人が受け入れられるものとしてマークを付ける必要があります。
Alex Osborn 2013年

1

Angular UI Utilsの 'if statement'ディレクティブを使用して問題を解決することをお勧めします(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.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" />

明らかに、上記のコードサンプルの全てが値1、値2、値3とvalue4の各々は、に等しいであろうと仮定されているnull/ false情報のあなたの4個のそれぞれが(のブール値にも、従って不完全である場合true、それらが完全である場合)。

PS。AngularUIプロジェクトは最近サブプロジェクトに分割されており、のドキュメントはui-if現在欠落しているようです(おそらくパッケージのどこかにあります)。ただし、ご覧のように使用するのは非常に簡単です。また、Angular UIプロジェクトにGithubの「問題」を記録して、チームにも指摘しました。

更新: 'ui-if'はコアAngularJSコードに統合されているため、AngularUIプロジェクトにはありません!ただし、現在「不安定」とマークされている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

これは2回だけループすることを許可し、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);
                }              
            });
            }
        }
    }
})();
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.