Gibt es eine Möglichkeit, auf einen Standard zurückzugreifen, wenn ein ngSrc-Pfad in einen 404-Pfad aufgelöst wird?


129

Für die Anwendung, die ich erstelle, muss mein Benutzer 4 Informationen festlegen, bevor dieses Bild überhaupt geladen werden kann. Dieses Bild ist das Herzstück der Anwendung, sodass der fehlerhafte Bildlink den Eindruck erweckt, dass das Ganze verfälscht ist. Ich hätte gerne ein anderes Bild auf einer 404.

Irgendwelche Ideen? Ich möchte vermeiden, eine benutzerdefinierte Direktive dafür zu schreiben.

Ich war überrascht, dass ich keine ähnliche Frage finden konnte, besonders wenn die erste Frage in den Dokumenten dieselbe ist!

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



Antworten:


252

Es ist eine ziemlich einfache Anweisung, auf einen Fehler beim Laden eines Bildes zu achten und den src zu ersetzen. (Plunker)

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);
        }
      });
    }
  }
});

Wenn Sie das Fehlerbild anzeigen möchten, wenn ngSrc leer ist, können Sie Folgendes hinzufügen (Plunker) :

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

Das Problem ist, dass ngSrc das src-Attribut nicht aktualisiert, wenn der Wert leer ist.


Funktioniert gut, wenn die URL fehlerhaft ist (404), aber wenn es sich um eine leere Zeichenfolge handelt, verschluckt ng-src den Fehler stillschweigend.
Stephen Patten

2
Wenn eine leere Zeichenfolge für Ihr Modell nicht gültig ist, stellen Sie sicher, dass der Ausdruck, an den Sie mit ng-src binden, keine leere Zeichenfolge zurückgibt.
Justin Lovero

Aktualisiertes Skript zur Unterstützung der Verwendung von srcAttributen für das err-srcBild: plnkr.co/edit/b05WtghBOHkxKdttZ8q5
Ryan Schumacher

@ JustinLovero Ich halte es für einen Fehler im Winkel und habe es gemeldet. Das Problem ist, dass ngSrc das src-Attribut nicht setzt, wenn der Wert leer ist. Dies könnte tatsächlich ein Problem sein, wenn Sie beispielsweise ein Telefon anzeigen und ein anderes Telefon mit Ajax laden, bei dem eine Bild-URL fehlt. Das Bild des alten Telefons wird weiterhin angezeigt, aber ich denke, ein Fehler oder Platzhalter wäre angemessener. Ja, Sie könnten in Ihrem Modell damit umgehen, aber das Verhalten beim Verlassen eines alten Bildes ist meiner Meinung nach falscher als das Anzeigen eines Fehlers.
Jason Goemaat

@ JasonGoemaat, wie würde ich das kaputte Bild durch Text ersetzen?
Simeg

48

Wenig spät zur Party, obwohl ich eine Lösung für mehr oder weniger das gleiche Problem in einem System gefunden habe, das ich baue.

Meine Idee war jedoch, JEDES Bild- imgTag global zu handhaben .

Ich wollte meine nicht HTMLmit unnötigen Anweisungen wie den err-srchier gezeigten überhäufen müssen. Sehr oft, besonders bei dynamischen Bildern, werden Sie nicht wissen, ob es fehlt, bis es zu spät ist. Das Hinzufügen zusätzlicher Anweisungen für die Möglichkeit, dass ein Bild fehlt, scheint mir übertrieben.

Stattdessen erweitere ich das vorhandene imgTag - worum es bei Angular-Direktiven eigentlich geht.

Also - das habe ich mir ausgedacht.

Hinweis : Dies setzt voraus, dass die vollständige JQuery-Bibliothek vorhanden ist und nicht nur die JQlite Angular-Bibliothek, mit der wir arbeiten.error()

Sie können es in Aktion an diesem Plunker sehen

Die Richtlinie sieht ungefähr so ​​aus:

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');
            });
        }
    }
});

Wenn ein Fehler auftritt (weil das Bild nicht existiert oder nicht erreichbar ist usw.), erfassen wir und reagieren. Sie können auch versuchen, die Bildgrößen zu ermitteln - wenn sie überhaupt auf dem Bild / Stil vorhanden waren. Wenn nicht, setzen Sie sich einen Standard.

In diesem Beispiel wird placehold.it verwendet, damit stattdessen ein Bild angezeigt wird.

Jetzt JEDES Bild, unabhängig von der Verwendung srcoder ng-srchat sich selbst abgedeckt, falls nichts geladen wird ...


2
sehr schöne Lösung! Lassen Sie uns diesen nach oben stimmen!
Matthijs

1
Das ist ziemlich schön, muss ich sagen. Ich möchte eigentlich gar nicht, dass ein Bild angezeigt wird, wenn es nicht aufgelöst wird. Deshalb habe ich Folgendes verwendet: element.css ("display", "none"); um es einfach total zu verstecken
Pompeius

1
nett :) oder Sie können es einfach mit element.remove () ganz aus dem DOM entfernen; :)
Darren Wainwright

Funktioniert super, danke!
Ecorvo

Ihre Lösung wurde ein wenig bearbeitet, sodass sie auch funktioniert, wenn der Pfad leer ist. stackoverflow.com/a/35884288/2014288
andrfas

21

Um die Jason-Lösung zu erweitern und beide Fälle eines Ladefehlers oder einer leeren Quellzeichenfolge abzufangen, können wir einfach eine Uhr hinzufügen.

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
Netter Code, aber es scheint ein Overkill zu sein, eine Uhr für das hinzuzufügen, was leicht ng-ifin der Vorlage überprüft werden kann (die leere src-Zeichenfolge)
alonisser

Es scheint, als könnten Sie ngSrc einfach durch Ihre eigene Direktive ersetzen und die Fehlerbindung festlegen, um errSrc anstelle einer separaten errSrc-Direktive zu verwenden, wenn Sie dies wünschen. Sie könnten verwenden attrs.$observe('ngSrc', function(value) {...});, das ist, was ngSrc intern anstelle von $ watch verwendet
Jason Goemaat

Das ganze Problem mit Leerzeichen besteht darin, dass ngSrc das src-Attribut nicht aktualisiert, wenn der Wert leer ist. Wenn Sie also eine eigene Direktive hätten, die ngSrc ersetzt, wäre keine Leerprüfung erforderlich.
Jason Goemaat

1
@Pokono - Sie können in der Fehlerfunktion überprüfen, ob das aktuelle Attribut 'src' mit 'errSrc' identisch ist.
user2899845

1
@BiswasKhayargoli - hat einen Aufruf zum Abbestellen hinzugefügt, so dass nach dem ersten Laden keine Verarbeitungskosten
anfallen

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;
            });
        }
    };
});

in HTML:

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

Ich denke, dies sollte die akzeptierte Antwort sein, da es nicht jQuery verwendet und das Problem löst
Gregra

10

Wenn image 404 ist oder image null leer ist, was auch immer keine Anweisungen erforderlich sind, können Sie einfach den ng-src-Filter wie diesen verwenden :)

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

2
Nein, wenn p.image bei einem AJAX-Aufruf einen 404 zurückgibt, wird dies als 'true' behandelt und nicht zum zweiten img / no-image.png übergegangen.
James Gentes

2
@ JamesGentes - Das scheint nicht der Fall zu sein. Diese Verwendung von ng-src funktioniert bei mir genau wie gezeigt. Und es ist so viel einfacher.
Shanemgrey

@ Shaneveeg danke, das ist interessant - ich frage mich, ob das Backend anders ist. Ich verwende Node mit Express, vielleicht wird es anders gehandhabt.
James Gentes

2
@ JamesGentes - Korrektur meines vorherigen Kommentars. Das OP sucht nach einer Lösung, wenn das Modell einen gültigen URI zurückgibt, nach einem 404, wenn dieser befolgt wird. In diesem Fall funktioniert diese Lösung nicht, sondern führt zu Bildbruch. Wenn Angular für den Bildwert nichts zurückgibt, wird der Fallback korrekt ausgewählt.
Shanemgrey

Ich benutze Laravel und es funktioniert perfekt für mich, wenn 404, null oder leer der Fall ist. aber vielleicht gibt Express eine Art Fehler anstelle einer Code-Antwort wie 404 zurück, also hängt ja wahrscheinlich vom Server-Framework ab
NeoNe

8

Ich benutze so etwas, aber es setzt voraus, dass team.logo gültig ist. Es erzwingt die Standardeinstellung, wenn "team.logo" nicht festgelegt oder leer ist.

<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
Dies ist richtig, da "team.logo" als Zeichenfolge (true / false) ausgewertet wird, während ng-src {{team.logo}} als true ansieht, selbst wenn eine 404 zurückgegeben wird.
James Gentes


1

Gibt es einen bestimmten Grund, warum Sie das Fallback-Image in Ihrem Code nicht deklarieren können?

Soweit ich weiß, haben Sie zwei mögliche Fälle für Ihre Bildquelle:

  1. Stellen Sie die Informationen richtig ein <4 = Fallback-Bild.
  2. Richtig eingestellte Informationen == 4 = Generierte URL.

Ich denke, dies sollte von Ihrer App erledigt werden. Wenn die richtige URL derzeit nicht ermittelt werden kann, übergeben Sie stattdessen eine URL für das Laden / Fallback / Platzhalterbild.

Der Grund dafür ist, dass Sie nie ein "fehlendes" Bild haben, da Sie explizit die richtige URL angegeben haben, die zu jedem Zeitpunkt angezeigt werden soll.


Entschuldigung, ich hätte klarstellen sollen, dass die URL auch dann, wenn alle 4 Werte festgelegt sind, 404 kann (der Benutzer kann unterwegs Ordner hinzufügen). In der Zwischenzeit habe ich eine Funktion hinzugefügt, die die Antwortheader der URL überprüft, wenn alle Werte vorliegen eingestellt sind. Es wäre immer noch schön, dies ohne besondere Behandlung zu handhaben, ich wette, ich werde wieder darauf stoßen :)
will_hardin

1
Cool, du solltest das als Antwort zeigen und es als akzeptiert für alle markieren, die in Zukunft suchen.
Alex Osborn

1

Ich schlage vor, dass Sie die Anweisung if if der Angular UI Utils verwenden möchten, um Ihr Problem zu lösen. Diese finden Sie unter http://angular-ui.github.io/. . Ich habe es gerade benutzt, um genau das Gleiche zu tun.

Dies ist ungetestet, aber Sie könnten etwas tun wie:

Controller-Code:

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

(oder einfacher)

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

HTML in Ansicht: <img ui-if="showImage()" ng-src="images/{{data.value}}.jpg" />

Oder noch einfacher: Sie können einfach eine Scope-Eigenschaft verwenden:

Controller-Code:

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

HTML in Ansicht: <img ui-if="showImage" ng-src="images/{{data.value}}.jpg" />

<img>Fügen Sie für ein Platzhalterbild einfach ein anderes ähnliches Tag hinzu, stellen Sie Ihrem ui-ifParameter jedoch ein Ausrufezeichen ( !) voran, und lassen Sie ngSrc entweder den Pfad zum Platzhalterbild haben, oder verwenden Sie einfach ein srcTag gemäß dem normalen alten HTML- Code .

z.B. <img ui-if="!showImage" src="images/placeholder.jpg" />

Offensichtlich gehen alle oben genannten Codebeispiele davon aus, dass jeder von Wert1, Wert2, Wert3 und Wert4 gleich null/ ist, falsewenn jede Ihrer 4 Informationen unvollständig ist (und somit auch einem booleschen Wert vontrue wenn sie vollständig sind).

PS. Das AngularUI-Projekt wurde kürzlich in Unterprojekte unterteilt, und die Dokumentation für ui-ifscheint derzeit zu fehlen (wahrscheinlich ist sie jedoch irgendwo im Paket enthalten). Wie Sie sehen, ist die Verwendung jedoch recht einfach, und ich habe ein Github-Problem im Angular UI-Projekt protokolliert, um das Team darauf aufmerksam zu machen.

UPDATE: 'ui-if' fehlt im AngularUI-Projekt, da es in den AngularJS-Kerncode integriert wurde! Nur ab Version 1.1.x, die derzeit als 'instabil' markiert ist.


ng-ifschaffte es übrigens in den Kern (ab 1.1.5, wenn ich mich nicht irre).
Holographisches Prinzip

1

Hier ist eine Lösung, die ich mit nativem Javascript gefunden habe. Ich überprüfe, ob das Bild beschädigt ist, füge dem Bild für alle Fälle eine Klasse hinzu und ändere die Quelle.

Ich habe einen Teil meiner Antwort von einer Quora-Antwort erhalten: 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

Kam mit meiner eigenen Lösung. Es ersetzt das Image sowohl, wenn src oder ngSrc leer sind, als auch wenn img 404 zurückgibt.

(Gabel der @ Darren-Lösung)

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

Dadurch kann nur zweimal eine Schleife ausgeführt werden. Um zu überprüfen, ob ng-src nicht vorhanden ist, verwenden Sie err-src. Dadurch wird verhindert, dass die Schleife fortgesetzt wird.

(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);
                }              
            });
            }
        }
    }
})();
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.