jeśli ścieżka ngSrc jest rozwiązywana do 404, czy istnieje sposób na powrót do wartości domyślnej?


130

Aplikacja, którą tworzę, wymaga od mojego użytkownika ustawienia 4 informacji, zanim ten obraz będzie miał szansę się załadować. Ten obraz jest centralnym elementem aplikacji, więc uszkodzony link do obrazu sprawia, że ​​wygląda na to, że wszystko jest zepsute. Chciałbym, aby inny obraz zajął jego miejsce na 404.

Jakieś pomysły? Chciałbym uniknąć pisania niestandardowej dyrektywy w tym zakresie.

Byłem zaskoczony, że nie mogłem znaleźć podobnego pytania, zwłaszcza gdy pierwsze pytanie w dokumentach jest takie samo!

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



Odpowiedzi:


252

Jest to dość prosta dyrektywa, aby obserwować błąd podczas ładowania obrazu i zastąpić src. (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);
        }
      });
    }
  }
});

Jeśli chcesz wyświetlić obraz błędu, gdy ngSrc jest pusty, możesz dodać to (Plunker) :

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

Problem polega na tym, że ngSrc nie aktualizuje atrybutu src, jeśli wartość jest pusta.


Działa dobrze, jeśli adres URL jest uszkodzony (404), ale jeśli jest to pusty ciąg, ng-src po cichu pochłania błąd.
Stephen Patten

2
Jeśli pusty ciąg nie jest prawidłowy dla twojego modelu, zrób to tak, aby wyrażenie, z którym łączysz się za pomocą ng-src, nie zwróciło pustego ciągu.
Justin Lovero,

Zaktualizowany skrypt, aby obsługiwał użycie srcatrybutu dla err-srcobrazu: plnkr.co/edit/b05WtghBOHkxKdttZ8q5
Ryan Schumacher

@JustinLovero Uważam, że to błąd w angular i zgłosiłem to. Problem polega na tym, że ngSrc nie ustawi atrybutu src, jeśli wartość jest pusta. Może to faktycznie stanowić problem, jeśli na przykład wyświetlasz telefon i ładujesz inny telefon z Ajax, który nie ma adresu URL obrazu. Obraz starego telefonu byłby nadal wyświetlany, ale myślę, że bardziej odpowiedni byłby błąd lub symbol zastępczy. Tak, poradziłbyś sobie z tym w swoim modelu, ale zachowanie polegające na pozostawieniu starego obrazu jest, moim zdaniem, bardziej niewłaściwe niż wyświetlanie błędu.
Jason Goemaat

@JasonGoemaat, jak miałbym zastąpić uszkodzony obraz tekstem?
simeg

48

Trochę późno na imprezę, chociaż wpadłem na rozwiązanie mniej więcej tego samego problemu w systemie, który buduję.

Moim pomysłem było jednak obsłużyć KAŻDY imgtag graficzny na całym świecie.

Nie chciałem obciążać mnie HTMLniepotrzebnymi dyrektywami, takimi jak err-srcte pokazane tutaj. Dość często, zwłaszcza w przypadku dynamicznych obrazów, nie wiadomo, czy brakuje, aż będzie za późno. Dodawanie dodatkowych dyrektyw w przypadku braku obrazu wydaje mi się przesadą.

Zamiast tego rozszerzam istniejący imgtag - o to tak naprawdę chodzi w dyrektywach Angulara.

A więc - oto co wymyśliłem.

Uwaga : Wymaga to obecności pełnej biblioteki JQuery, a nie tylko JQlite Angular, ponieważ używamy.error()

Możesz zobaczyć to w akcji w tym Plunkerze

Dyrektywa wygląda mniej więcej tak:

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

Gdy wystąpi błąd (który będzie spowodowany tym, że obraz nie istnieje lub jest nieosiągalny itp.) Przechwytujemy i reagujemy. Możesz też spróbować uzyskać rozmiary obrazu - jeśli były obecne na obrazku / stylu w pierwszej kolejności. Jeśli nie, ustaw siebie jako domyślną.

W tym przykładzie zamiast obrazu wyświetlany jest obraz placehold.it.

Teraz KAŻDY obraz, niezależnie od tego, srcczy ng-srczostał użyty, czy też został zakryty na wypadek, gdyby nic się nie załadowało ...


2
bardzo fajne rozwiązanie! oddajmy ten głos na szczyt!
Matthijs,

1
Muszę przyznać, że jest to całkiem piękne. Właściwie nie chcę, aby jakikolwiek obraz był wyświetlany, jeśli się nie rozwiąże, więc użyłem: element.css ("display", "none"); po prostu to całkowicie ukryć
Pompey

1
ładne :) lub po prostu możesz po prostu całkowicie usunąć go z DOM, za pomocą elementu.remove (); :)
Darren Wainwright

Działa świetnie, dzięki!
ecorvo

Edytowałem trochę Twoje rozwiązanie, więc działa również, gdy ścieżka jest pusta. stackoverflow.com/a/35884288/2014288
andrfas

21

Aby rozwinąć rozwiązanie Jasona w celu wychwycenia obu przypadków błędu ładowania lub pustego ciągu źródłowego, możemy po prostu dodać zegarek.

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
Niezły kod, ale wydaje się przesadą, aby dodać zegarek dla tego, co można łatwo sprawdzić ng-ifw szablonie (pusty ciąg src)
alonisser

Wygląda na to, że możesz po prostu zastąpić ngSrc własną dyrektywą i ustawić wiązanie w przypadku błędu, aby używał errSrc zamiast oddzielnej dyrektywy errSrc, jeśli tego chcesz. Możesz użyć attrs.$observe('ngSrc', function(value) {...});, to jest to, czego używa ngSrc wewnętrznie zamiast $ watch
Jason Goemaat

Cały problem ze spacjami polega na tym, że ngSrc nie aktualizuje atrybutu src, jeśli wartość jest pusta, więc gdybyś miał własną dyrektywę zastępującą ngSrc, nie wymagałoby to pustego czeku.
Jason Goemaat

1
@Pokono - możesz sprawdzić wewnątrz funkcji błędu, czy aktualny atrybut „src” jest taki sam jak „errSrc”.
user2899845

1
@BiswasKhayargoli - dodano wezwanie do anulowania subskrypcji, więc nie będzie to miało żadnych kosztów przetwarzania po początkowym załadowaniu
użytkownik2899845

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

w HTML:

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

Uważam, że to powinna być akceptowana odpowiedź, ponieważ nie używa ona jQuery i rozwiązuje problem
Gregra

10

Jeśli obraz jest 404 lub obraz jest pusty, cokolwiek nie jest potrzebne dyrektyw, możesz po prostu użyć filtru ng-src w ten sposób :)

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

2
Nie, jeśli p.image zwróci 404 w wywołaniu AJAX, potraktuje to jako „prawda” i nie przejdzie do drugiego img / no-image.png.
James Gentes,

2
@JamesGentes - Wydaje się, że tak nie jest. To użycie ng-src działa dla mnie dokładnie tak, jak pokazano. I to jest o wiele prostsze.
shanemgrey

@shaneveeg dzięki, to interesujące - zastanawiam się, czy zaplecze jest inne. Używam Node z Expressem, być może obsługuje to inaczej.
James Gentes

2
@JamesGentes - Korekta mojego poprzedniego komentarza. OP szuka rozwiązania, gdy model zwraca prawidłowy identyfikator URI, ale 404, gdy jest śledzony. W takim przypadku to rozwiązanie nie działa, skutkuje to zepsutym obrazem. Jeśli angular nie zwraca nic dla wartości obrazu, to prawidłowo wybiera rezerwę.
shanemgrey

Używam Laravel i działa idealnie dla mnie, jeśli 404, null lub pusty jest przypadek. ale może Express zwraca jakiś błąd zamiast odpowiedzi kodu, jak 404, więc tak, prawdopodobnie zależy od struktury serwera
NeoNe

8

Używam czegoś takiego, ale zakładam, że logo team.logo jest prawidłowe. Wymusza ustawienie domyślne, jeśli „team.logo” nie jest ustawione lub jest puste.

<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
To jest poprawne, ponieważ ocenia „team.logo” jako ciąg (prawda / fałsz), podczas gdy ng-src zobaczy {{team.logo}} jako prawdziwe, nawet jeśli zwróci 404.
James Gentes,


1

Czy istnieje konkretny powód, dla którego nie możesz zadeklarować obrazu zastępczego w swoim kodzie?

Jak rozumiem, masz dwa możliwe przypadki źródła obrazu:

  1. Poprawnie ustaw informacje <4 = obraz zastępczy.
  2. Prawidłowo ustawione informacje == 4 = wygenerowany adres URL.

Myślę, że powinna to obsługiwać Twoja aplikacja - jeśli obecnie nie można określić prawidłowego adresu URL, zamiast tego podaj adres URL obrazu wczytywania / zastępczego / zastępczego.

Powodem jest to, że nigdy nie masz „brakującego” obrazu, ponieważ wyraźnie zadeklarowałeś poprawny adres URL do wyświetlenia w dowolnym momencie.


Przepraszam, powinienem był wyjaśnić, że nawet jeśli wszystkie 4 wartości są ustawione, adres URL nadal może 404 (użytkownik może dodawać foldery po drodze). W międzyczasie dodałem funkcję, która sprawdza nagłówki odpowiedzi adresu URL, gdy wszystkie wartości są ustawione. Nadal byłoby miło poradzić sobie z tym bez specjalnego traktowania, założę się, że znów się na to
natknę

1
Super, powinieneś to pokazać jako odpowiedź i oznaczyć ją jako zaakceptowaną dla każdego, kto będzie szukał w przyszłości.
Alex Osborn

1

Sugeruję, abyś mógł skorzystać z dyrektywy „if statement” w narzędziach Angular UI Utils, aby rozwiązać problem, którą można znaleźć na stronie http://angular-ui.github.io/ . Właśnie użyłem go, aby zrobić dokładnie to samo.

To nie jest testowane, ale możesz zrobić coś takiego:

Kod kontrolera:

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

(lub prostsze)

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

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

Lub jeszcze prościej, możesz po prostu użyć właściwości zakresu:

Kod kontrolera:

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

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

W przypadku obrazu zastępczego wystarczy dodać kolejny podobny <img>znacznik, ale poprzedzić ui-ifparametr !znakiem wykrzyknika ( ) i albo ustawić w ngSrc ścieżkę do obrazu zastępczego, albo po prostu użyć srctagu, jak w zwykłym starym języku HTML.

na przykład. <img ui-if="!showImage" src="images/placeholder.jpg" />

Oczywiście wszystkie powyższe przykłady kodu zakładają, że każda z wartości 1, wartość2, wartość3 i wartość4 będzie równa się null/ falsekiedy każda z 4 informacji jest niekompletna (a zatem także wartością logiczną, truekiedy są kompletne).

PS. Projekt AngularUI został niedawno podzielony na podprojekty, a dokumentacji ui-ifwydaje się, że obecnie brakuje (prawdopodobnie jest ona gdzieś w pakiecie). Jest jednak dość prosty w użyciu, jak widać, i zarejestrowałem „problem” na Github w projekcie Angular UI, aby wskazać go zespołowi.

AKTUALIZACJA: w projekcie AngularUI brakuje „ui-if”, ponieważ zostało ono zintegrowane z podstawowym kodem AngularJS! Jednak tylko od wersji 1.1.x, która jest obecnie oznaczona jako „niestabilna”.


ng-ifzrobiłem to w rdzeniu przy okazji (od 1.1.5, jeśli się nie mylę).
zasada holograficzna,

1

Oto rozwiązanie, które wymyśliłem, używając natywnego javascript. Sprawdzam, czy obraz jest uszkodzony, a następnie na wszelki wypadek dodaję klasę do obrazu i zmieniam źródło.

Część mojej odpowiedzi pochodzi z odpowiedzi 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

Przyszło z moim własnym rozwiązaniem. Zastępuje obraz zarówno, jeśli src, jak i ngSrc jest puste, i jeśli img zwraca 404.

(widelec rozwiązania @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

Pozwoli to tylko na dwukrotne zapętlenie, aby sprawdzić, czy ng-src nie istnieje, w przeciwnym razie użyj err-src, co zapobiega kontynuowaniu pętli.

(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);
                }              
            });
            }
        }
    }
})();
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.