background default image if ng-style image loaded is invalid url - angularjs

I am adding background images to my div like this
ng-style="{'background-image' : 'url('+ myvariable.for.image +')'}">
where myvariable.for.image is a url like /examplesite/image/id
This works fine with one exception, if the image is not there it just doesnt do anything and my background looks too bla...If the image doesnt exist I want to be able to replace it with a default image.
But I cant seem to figure out how

Instead of ngStyle I'd use a custom directive for this. Such as the following. This checks to see if an attribute is provided, if so it attempts to load that image. If it loads an image then we set the background image to it, otherwise we use a default image.
myApp.directive('bgImage', function () {
return {
link: function(scope, element, attr) {
attr.$observe('bgImage', function() {
if (!attr.bgImage) {
// No attribute specified, so use default
element.css("background-image","url("+scope.defaultImage+")");
} else {
var image = new Image();
image.src = attr.bgImage;
image.onload = function() {
//Image loaded- set the background image to it
element.css("background-image","url("+attr.bgImage+")");
};
image.onerror = function() {
//Image failed to load- use default
element.css("background-image","url("+scope.defaultImage+")");
};
}
});
}
};
});
Used like this:
<div bg-image="{{person.src}}">
demo fiddle

<div err-bg-src='{{default_business_logo_wd}}' ng-style="{'background-image' : 'url('+ifNull(place.logo_wd,default_business_logo_wd)+')'}" id="perfilEstablecimiento-container10" class="place-header">
<div id="perfilEstablecimiento-container13" class="place-title-container">
<h4 id="perfilEstablecimiento-heading1" class="place-title">{{place.name}}</h4>
</div>
</div>
Using a $timeout inside that custom directive worked for me.
.directive
(
'errBgSrc',
function($timeout)
{
return {
link: function(scope, element, attrs)
{
$timeout
(
function()
{
if(window.getComputedStyle(document.getElementById(attrs.id)).backgroundImage=='none'||window.getComputedStyle(document.getElementById(attrs.id)).backgroundImage==null)
{
document.getElementById(attrs.id).style.backgroundImage='url('+attrs.errBgSrc+')';
}
else
{
var image = new Image();
var style=window.getComputedStyle(document.getElementById(attrs.id)).backgroundImage;
var url=style.slice(5,style.length-2);
image.src = url;
image.onerror = function()
{
document.getElementById(attrs.id).style.backgroundImage='url('+attrs.errBgSrc+')';
};
}
},
500
);
}
}
}
)

Related

angularjs set background-image

I have a div to visualize progress.
Therefor I have this ng-style definition:
ng-style="{'background-image':'linear-gradient(left, rgba(255,0,0,1)'+bufferProgressPercent!=='NaN'?bufferProgressPercent:0+'%,rgba(0,0,0,0)'+(bufferProgressPercent!=='NaN'?bufferProgressPercent:0)+'%)!important'};"
//output in developer-tools
<div class="audio-controls-wrapper" ng-style="{'backgroundImage':'linear- gradient(left, rgba(255,0,0,1)'+bufferProgressPercent!=='NaN'? bufferProgressPercent:0+'%,rgba(0,0,0,0)'+(bufferProgressPercent!=='NaN'? bufferProgressPercent:0)+'%)'};">
The document only shows the as clear-text. The values don't get rendered.
The values are correct:
{{'background-image:linear-gradient(left, rgba(255,0,0,1)'+ (bufferProgressPercent!=='NaN'?bufferProgressPercent:0)+'%,rgba(0,0,0,0)'+(bufferProgressPercent!=='NaN'?bufferProgressPercent:0)+'%)'}}
Gives this out:
background-image:linear-gradient(left, rgba(255,0,0,1)59%,rgba(0,0,0,0)59%)
Another attempt was to create a directive:
<div class="audio-controls-wrapper" progress-animation="bufferProgressPercent" >
Directive:
scope.$watch('progressAnimation', function(current, old){
if(angular.isDefined(current) && current !== old){
var backgroundImage = 'linear-gradient(left, rgba(255,0,0,1)'+ (current!=='NaN'?current:0)+'%,rgba(0,0,0,0)'+(current!=='NaN'? current:0)+'%)!important';
//scope.$applyAsync(function(){
//element.css({'backgroundImage':backgroundImage});
element[0].style.backgroundImage = backgroundImage;
$compile(element.contents())(scope);
//});
console.log(backgroundImage)
console.log(element[0].style)
}
});
But the attribute backgroundImage of this element is never set.
Did you enter the watch function into directive ?
for example
.directive('progressAnimation', function () {
return {
restrict: 'A', //E = element, A = attribute, C = class, M = comment
scope: { // input the var that you want to watch
},
link: function ($scope, element, attrs) {
//put your watch function here
if(angular.isDefined(current) && current !== old){
var backgroundImage = 'linear-gradient(left, rgba(255,0,0,1)'+ (current!=='NaN'?current:0)+'%,rgba(0,0,0,0)'+(current!=='NaN'? current:0)+'%)!important';
element[0].style.backgroundImage = backgroundImage;
$compile(element.contents())(scope);
console.log(backgroundImage)
console.log(element[0].style)
}
} //DOM manipulation
}
});
To set the background in I had to use
background
instead of
background-image
further I had to replace
linear-gradient(...
with
-moz-linear-gradient(
that means
{{'background-image:linear-gradient(left, rgba(255,0,0,1)'+ (bufferProgressPercent!=='NaN'?bufferProgressPercent:0)+'%,rgba(0,0,0,0)'+(bufferProgressPercent!=='NaN'?bufferProgressPercent:0)+'%)'}}
became this:
{{'background:-moz-linear-gradient((left, rgba(255,0,0,1)'+ (bufferProgressPercent!=='NaN'?bufferProgressPercent:0)+'%,rgba(0,0,0,0)'+(bufferProgressPercent!=='NaN'?bufferProgressPercent:0)+'%)'}}
I guess I have to add all the browser besides chrom a certain linear gradient.

Floating title is not working in Angularjs

I have a list of item with two iterations. I want a sticky title when the title scroll up from the view area. I have done it with jquery, but can't able to do in angular. Created a fiddle https://jsfiddle.net/1vf5ska7/
I just to want to add a class in tag when the title is goes up to the view area.
angular.element(document.querySelector('#l-content__desc__split1__body')).on('scroll', function() {
});
And the important thing is it is not a window scroll. It's a div scroll
Please help me.
Thanks..
You need to include a directive and operate on it. If $window.pageYOffset is greater than the position of the element you apply a specific class to that element which is positioned fixed.
var app = angular.module('app', []);
app.directive('setClassOnTop', function ($window) {
var $win = angular.element($window); // wrap window object as jQuery object
return {
restrict: 'A',
link: function (scope, element, attrs) {
var title = angular.element(document.getElementById('sticky-title'));
var offsetTop = title[0].offsetTop;
$win.on('scroll', function (e) {
if ($window.pageYOffset > offsetTop) {
angular.element(title[0]).addClass('floating-title');
} else {
angular.element(title[0]).removeClass('floating-title');
}
});
}
};
});
And here is the updated fiddle:
https://jsfiddle.net/1vf5ska7/3/

Angular JS conditional logic to check "class name" exists

Below Angular JS code works fine on Mouseover & Mouseout. Need help regard adding conditional logic on JS code.
If class name "active" exists, img src path have to be in "overImg" even if user mouseover & mouseout. But, present behaviour removes overImg once user mouseout from element. Active state have to be different from the rest of navigation element.
AngularJS:
.directive('eleHoverAction', function() {
return {
link: function (scope, elem, attrs) {
var imgObj = $(elem).find('img');
var upImg = attrs.eleUpImgSrc;
var overImg = attrs.eleOverImgSrc;
elem.bind('mouseover', function () {
$(imgObj).attr("src", overImg);
scope.$apply();
});
elem.bind('mouseout', function() {
$(imgObj).attr("src", upImg);
scope.$apply();
});
}
};
});
HTML:
<li class="menu-item menu-item--category active" ele-hover-action ele-up-img-src="images/test1.png" ele-over-img-src="images/test1-over.png">
<img src="images/test1.png" oversrc="images/test1-over.png" alt=""/><span>Test1</span>
</li>
<li class="menu-item menu-item--category" ele-hover-action ele-up-img-src="images/test2.png" ele-over-img-src="images/test2-over.png">
<img src="images/test2.png" oversrc="images/test2-over.png" alt=""/><span>Test2</span>
</li>
The most obvious method is to add an if statement to your "mouseout" handler that checks if the element hasClass active:
.directive('eleHoverAction', function() {
return {
link: function (scope, elem, attrs) {
var imgObj = elem.find('img');
var upImg = attrs.eleUpImgSrc;
var overImg = attrs.eleOverImgSrc;
elem.bind('mouseover', function () {
imgObj.attr("src", overImg);
scope.$apply();
});
elem.bind('mouseout', function() {
if (!elem.hasClass("active")) {
imgObj.attr("src", upImg);
}
scope.$apply();
});
if (elem.hasClass("active")) {
imgObj.attr("src", overImg);
} else {
imgObj.attr("src", upImg);
}
}
};
});
I went ahead and set the src attribute of the image based on the directive attributes. You could just take that part out if you don't want it. Also, wrapping elem in a jQuery call is redundant because Angular elements are already wrapped in either jQuery (if available when Angular loads) or its own jQLite. Otherwise, you wouldn't be able to call elem.bind.
Try it in a fiddle.

angularjs display default image if original does not exists

I want to show user image if image exist, like :
<img alt="user_image" src="/images/profile_images/{{result.image}}">
And if user image not available then should show default image , Like
<img alt="user_image" src="/images/default_user.jpg">
How can I do this through angularjs in html page ?
You could create a directive checkImage to check if the image really exist:
<img check-image ng-src="{{img}}" alt="Image" width="100%" >
Directive:
myApp.directive('checkImage', function($http) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
attrs.$observe('ngSrc', function(ngSrc) {
$http.get(ngSrc).success(function(){
alert('image exist');
}).error(function(){
alert('image not exist');
element.attr('src', '/images/default_user.jpg'); // set default image
});
});
}
};
});
See the demo http://jsfiddle.net/manzapanza/dtt1z5p8/
You can pass in an expression to the alt attribute:
<img alt="{{user_image}}" src="/images/profile_images/{{result.image}}">
Edit: Doh, misread your question. In your controller (or the service which provides the image to the controller), you can set result.image to the user's image or the default image.
Another option is to use conditional logic in ng-src:
<img ng-src="{{ result.image || 'default_user.jpg' }}"/>
This will set the src to result.image if it exists, otherwise to the default.
Personally, I like the first option better since it keeps logic out of the view, but I bet you could find someone who would argue that it belongs in the view since it is view logic.
Edit 2: here is a plnkr: http://plnkr.co/edit/vfYU8T3PlzjQPxAaC5bK?p=preview
The previously mentioned solutions will double the HTTP requests. Every image will be loaded twice!
This solution does not reload the image just to see if it exists:
myApp.directive('checkImage', function() {
return {
link: function(scope, element, attrs) {
element.bind('error', function() {
element.attr('src', '/images/default_user.jpg'); // set default image
});
}
}
});
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;
});
}
};
});
If you are using Ionic 2 you can try this, it's worked for me:
<img src="{{data.imgPath || 'assets/img/empty.png'}}" >
Just for fun ,
Angular 2 is on the way . According the specification ngIf may solve in any directive .
There is no need to complex directives . Build a class checks for the result object which have method check image is exists as #manzapanza did.
Build a class definition as follows :
<div *ngIf=result.isImageExists()>
<img [src]="result.image" />
</div>
<div *ngIf=!result.isImageExists()>
<img [src]="result.defaultImage" />
</div>
I think new angular is better and more clear.
Hope Helpes!!;

if a ngSrc path resolves to a 404, is there a way to fallback to a default?

The application I'm building requires my user to set 4 pieces of information before this image even has a chance of loading. This image is the center-piece of the application, so the broken image link makes it look like the whole thing is borked. I'd like to have another image take its place on a 404.
Any ideas? I'd like to avoid writing a custom directive for this.
I was surprised that I couldn't find a similar question, especially when the first question in the docs is the same one!
http://docs.angularjs.org/api/ng.directive:ngSrc
It's a pretty simple directive to watch for an error loading an image and to replace the 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);
}
});
}
}
});
If you want to display the error image when ngSrc is blank you can add this (Plunker):
attrs.$observe('ngSrc', function(value) {
if (!value && attrs.errSrc) {
attrs.$set('src', attrs.errSrc);
}
});
The problem is that ngSrc doesn't update the src attribute if the value is blank.
Little late to the party, though I came up with a solution to more or less the same issue in a system I'm building.
My idea was, though, to handle EVERY image img tag globally.
I didn't want to have to pepper my HTML with unnecessary directives, such as the err-src ones shown here. Quite often, especially with dynamic images, you won't know if it's missing until its too late. Adding extra directives on the off-chance an image is missing seems overkill to me.
Instead, I extend the existing img tag - which, really, is what Angular directives are all about.
So - this is what I came up with.
Note: This requires the full JQuery library to be present and not just the JQlite Angular ships with because we're using .error()
You can see it in action at this Plunker
The directive looks pretty much like this:
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');
});
}
}
});
When an error occurs (which will be because the image doesn't exist or is unreachable etc) we capture and react. You can attempt to get the image sizes too - if they were present on the image/style in the first place. If not, then set yourself a default.
This example is using placehold.it for an image to show instead.
Now EVERY image, regardless of using src or ng-src has itself covered in case nothing loads up...
To expand Jason solution to catch both cases of a loading error or an empty source string, we can just add a watch.
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);
}
}
});
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}}" />
If image is 404 or image is null empty whatever there is no need for directives you can simply use ng-src filter like this :)
<img ng-src="{{ p.image || 'img/no-image.png' }}" />
I use something like this, but it assumes that team.logo is valid. It forces default if "team.logo" isn't set or is empty.
<img ng-if="team.logo" ng-src="https://api.example.com/images/{{team.logo}}">
<img ng-hide="team.logo" ng-src="img/default.png">
You don't need angular for that, or even CSS or JS. If you want, you can wrap this answer (linked) in a simple directive to make it simpler, like or something, but it's a pretty simple process... just wrap it in an object tag...
How to hide image broken Icon using only CSS/HTML (without js)
Is there a specific reason you can't declare the fallback image in your code?
As I understand, you have two possible cases for your image source:
Correctly set pieces of information < 4 = Fallback image.
Correctly set pieces of information == 4 = Generated URL.
I think this should be handled by your app - if the correct URL cannot currently be determined, instead pass a loading/fallback/placeholder image URL.
The reasoning is that you never have a 'missing' image, because you have explicitly declared the correct URL to display at any point in time.
I suggest that you might like to use the Angular UI Utils 'if statement' directive to solve your problem, as found at http://angular-ui.github.io/. I have just used it to do exactly the same thing.
This is untested, but you could do something like:
Controller code:
$scope.showImage = function () {
if (value1 && value2 && value3 && value4) {
return true;
} else {
return false;
}
};
(or simpler)
$scope.showImage = function () {
return value1 && value2 && value3 && value4;
};
HTML in View: <img ui-if="showImage()" ng-src="images/{{data.value}}.jpg" />
Or even simpler, you could just use a scope property:
Controller code:
$scope.showImage = value1 && value2 && value3 && value4;
HTML in View: <img ui-if="showImage" ng-src="images/{{data.value}}.jpg" />
For a placeholder image, just add another similar <img> tag but prepend your ui-if parameter with an exclamation (!) mark, and either make ngSrc have the path to the placeholder image, or just use a src tag as per normal ol' HTML.
eg. <img ui-if="!showImage" src="images/placeholder.jpg" />
Obviously, all of the above code samples are assuming that each of value1, value2, value3 and value4 will equate to null / false when each of your 4 pieces of information are incomplete (and thus also to a boolean value of true when they are complete).
PS. The AngularUI project has recently been broken in to sub-projects, and the documentation for ui-if seems to be missing currently (it's probably in the package somewhere though). However, it is pretty straightforward to use as you can see, and I have logged a Github 'issue' on the Angular UI project to point it out to the team too.
UPDATE: 'ui-if' is missing from the AngularUI project because it's been integrated in to the core AngularJS code! Only as of v1.1.x though, which is currently marked as 'unstable'.
Here's a solution I came up with using native javascript. I'm checking if the image is broken then adding a class to the image just in case and changing the source.
I got part of my answer from a Quora answer 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';
};
}
}
});
Came up with my own solution.
It replaces image both if src or ngSrc is empty, and if img returns 404.
(fork of #Darren solution)
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);
}
}
}
});
This will allow only to loop twice, to check if the ng-src doesn't exist else use the err-src, this prevents the continues looping.
(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);
}
});
}
}
}
})();
This is a solution to your problem
Do this in your component.html
<img [src]="imageUrl" (error)="handleError()">
in your component.ts file
defaultImageUrl = '/assets/default-image.png';
imageUrl = 'https://example.com/actual-image.png';
create a function inside the component.ts file
handleError() {
this.imageUrl = this.defaultImageUrl;
}
this will solve your issues for you

Resources