если путь ngSrc разрешается до 404, есть ли способ вернуться к значению по умолчанию?

129

Приложение, которое я создаю, требует, чтобы мой пользователь установил 4 элемента информации, прежде чем это изображение даже сможет загрузиться. Это изображение является центральным элементом приложения, поэтому из-за неработающей ссылки на изображение может показаться, что все это работает. Я бы хотел, чтобы другое изображение заняло место на 404.

Любые идеи? Я бы не хотел писать для этого специальную директиву.

Я был удивлен, что не смог найти подобный вопрос, особенно когда первый вопрос в документации такой же!

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

will_hardin
источник
Это может быть актуально: stackoverflow.com/q/13781685/1218080
голографический принцип
связанные: stackoverflow.com/questions/27549134/…
user2954463 01

Ответы:

252

Это довольно простая директива, позволяющая следить за ошибкой при загрузке изображения и заменять файл 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);
        }
      });
    }
  }
});

Если вы хотите отображать изображение ошибки, когда ngSrc пуст, вы можете добавить это (Plunker) :

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

Проблема в том, что ngSrc не обновляет атрибут src, если значение пустое.

Джейсон Гоэмаат
источник
Хорошо работает, если URL-адрес сломан (404), но если это пустая строка, ng-src молча проглатывает ошибку.
Стивен Паттен
2
Если пустая строка недопустима для вашей модели, сделайте так, чтобы выражение, к которому вы привязываете с помощью ng-src, не возвращало пустую строку.
Джастин Ловеро
Обновлен скрипт для поддержки использования srcатрибута для err-srcизображения: plnkr.co/edit/b05WtghBOHkxKdttZ8q5
Райан Шумахер,
@JustinLovero Я считаю это ошибкой в ​​angular и сообщил об этом. Проблема в том, что ngSrc не установит атрибут src, если значение пустое. На самом деле это может быть проблемой, если, например, вы показываете телефон и загружаете другой телефон с помощью ajax, у которого отсутствует URL-адрес изображения. Изображение старого телефона продолжало бы отображаться, но я думаю, что ошибка или заполнитель были бы более подходящими. Да, вы могли бы справиться с этим в своей модели, но я думаю, что поведение при оставлении старого изображения более неверно, чем отображение ошибки.
Джейсон Гоэмаат
@JasonGoemaat, как мне заменить сломанное изображение текстом?
simeg 04
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.

Теперь КАЖДОЕ изображение, независимо от того, используется оно srcили ng-srcсамо закрыто на случай, если ничего не загружается ...

Даррен Уэйнрайт
источник
2
очень красивое решение! проголосуем за это на вершину!
Matthijs
1
Должен сказать, это довольно красиво. На самом деле я вообще не хочу, чтобы изображение отображалось, если оно не разрешается, поэтому я использовал: element.css ("display", "none"); чтобы просто скрыть это полностью
Помпей
1
красиво :) или вы можете просто удалить его из DOM с помощью element.remove (); :)
Даррен Уэйнрайт
Отлично работает, спасибо!
ecorvo
Немного отредактировал ваше решение, поэтому оно работает также, когда путь пуст. stackoverflow.com/a/35884288/2014288
andrfas 09
21

Чтобы расширить решение Джейсона, чтобы поймать оба случая ошибки загрузки или пустой исходной строки, мы можем просто добавить часы.

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

    }
  }
});
user2899845
источник
2
Хороший код, но кажется излишним добавить наблюдение за тем, что можно легко проверить ng-ifв шаблоне (пустая строка src)
alonisser
Похоже, вы можете просто заменить ngSrc своей собственной директивой и настроить привязку при ошибке, чтобы использовать errSrc вместо отдельной директивы errSrc, если вы этого хотите. Вы можете использовать attrs.$observe('ngSrc', function(value) {...});, это то, что ngSrc использует для внутренних целей вместо $ watch
Джейсон Гоэмаат
Вся проблема с пробелами заключается в том, что ngSrc не обновляет атрибут src, если значение пустое, поэтому, если у вас есть собственная директива, заменяющая ngSrc, вам не потребуется пустая проверка.
Jason Goemaat
1
@Pokono - вы можете проверить внутри функции ошибки, совпадает ли текущий атрибут 'src' с 'errSrc'.
user2899845
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}}" />
Mehul
источник
Я считаю, что это должен быть принятый ответ, поскольку он не использует jQuery и решает проблему
Грегра
10

Если изображение 404 или изображение пусто, что бы там ни было, вы можете просто использовать фильтр ng-src следующим образом :)

<img ng-src="{{ p.image || 'img/no-image.png' }}" />
NeoNe
источник
2
Нет, если p.image возвращает 404 при вызове AJAX, он будет рассматривать это как «истину» и не перейдет ко второму img / no-image.png.
Джеймс Гентес,
2
@JamesGentes - Похоже, это не так. Такое использование ng-src работает для меня в точности так, как показано. И это намного проще.
шанемгрей
@shaneveeg, спасибо, это интересно - интересно, а задняя часть в чем-то отличается. Я использую Node с Express, возможно, он работает по-другому.
Джеймс Гентес
2
@JamesGentes - Исправление моего предыдущего комментария. OP ищет решение, когда модель возвращает действительный URI, но при этом - 404. В этом случае это решение не работает, это приводит к битому изображению. Если angular ничего не возвращает для значения изображения, это правильно выбирает откат.
shanemgrey
Я использую 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">
evandentremont
источник
2
Это правильно, поскольку он оценивает "team.logo" как строку (истина / ложь), тогда как ng-src будет рассматривать {{team.logo}} как истину, даже если он возвращает 404.
Джеймс Гентес,
2

Для этого вам не нужны ни angular, ни даже CSS или JS. Если вы хотите, вы можете обернуть этот ответ (связанный) в простую директиву, чтобы упростить, вроде или что-то в этом роде, но это довольно простой процесс ... просто оберните его в тег объекта ...

Как скрыть сломанный значок изображения, используя только CSS / HTML (без js)

Ник Стил
источник
1

Есть ли конкретная причина, по которой вы не можете объявить резервное изображение в своем коде?

Насколько я понимаю, у вас есть два возможных случая для вашего источника изображения:

  1. Правильно установленные фрагменты информации <4 = Резервное изображение.
  2. Правильно заданные фрагменты информации == 4 = Сгенерированный URL.

Я думаю, что это должно обрабатываться вашим приложением - если правильный URL-адрес в настоящее время не может быть определен, вместо этого передайте URL-адрес изображения загрузки / возврата / заполнителя.

Причина в том, что у вас никогда не бывает «отсутствующего» изображения, потому что вы явно указали правильный URL-адрес для отображения в любой момент времени.

Алекс Осборн
источник
Извините, мне следовало пояснить, что даже если установлены все 4 значения, URL-адрес может по-прежнему 404 (пользователь может добавлять папки по пути). Тем временем я добавил функцию, которая проверяет заголовки ответа URL-адреса, когда все значения установлены. Было бы неплохо справиться с этим без особого обращения, держу пари, я снова
столкнусь с
1
Отлично, вы должны показать это как ответ и пометить его как принятый для всех, кто будет искать в будущем.
Алекс Осборн
1

Я предлагаю вам использовать директиву if statement в Angular UI Utils для решения вашей проблемы, которую можно найти на 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" />

Очевидно, что все приведенные выше примеры кода предполагают, что каждое из значений value1, value2, value3 и value4 будет приравниваться к null/, falseкогда каждая из ваших 4 частей информации является неполной (и, следовательно, также с логическим значением, trueкогда они завершены).

PS. Проект AngularUI недавно был разбит на подпроекты, и документация для него, ui-ifпохоже, в настоящее время отсутствует (хотя она, вероятно, находится где-то в пакете). Однако, как вы можете видеть, его довольно просто использовать, и я зарегистрировал проблему Github в проекте пользовательского интерфейса Angular, чтобы указать на нее и команде.

ОБНОВЛЕНИЕ: ui-if отсутствует в проекте AngularUI, потому что он интегрирован в основной код AngularJS! Только начиная с версии 1.1.x, которая в настоящее время помечена как «нестабильная».

Мэтти Дж.
источник
ng-ifсделал это в ядре кстати (по состоянию на 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);
        }
    }
}
});
andrfas
источник
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);
                }              
            });
            }
        }
    }
})();
Брайан Ким Артификио
источник