[css] angularjs: ng-src equivalent for background-image:url(...)

In angularjs you have the tag ng-src which has the purpose that you won't receive an error for an invalid url before angularjs gets to evaluate the variables placed in between {{ and }}.

The problem is that I use quite some DIV's with a background-image set to an url. I do this because of the excellent CSS3 property background-size which crops the image to the exact size of the DIV.

The only problem is that I receive a lot of errors for the exact same reason they created a ng-src tag: I have some variables in the url and the browser thinks the image doesn't exist.

I realize that there is a possibility of writing a crude {{"style='background-image:url(myVariableUrl)'"}}, but this seems 'dirty'.

I've searched a lot and can't find the right way to do this. My app is becoming a mess because of all of these errors.

This question is related to css angularjs

The answer is


The above answer doesn't support observable interpolation (and cost me a lot of time trying to debug). The jsFiddle link in @BrandonTilley comment was the answer that worked for me, which I'll re-post here for preservation:

app.directive('backImg', function(){
    return function(scope, element, attrs){
        attrs.$observe('backImg', function(value) {
            element.css({
                'background-image': 'url(' + value +')',
                'background-size' : 'cover'
            });
        });
    };
});

Example using controller and template

Controller :

$scope.someID = ...;
/* 
The advantage of using directive will also work inside an ng-repeat :
someID can be inside an array of ID's 
*/

$scope.arrayOfIDs = [0,1,2,3];

Template :

Use in template like so :

<div back-img="img/service-sliders/{{someID}}/1.jpg"></div>

or like so :

<div ng-repeat="someID in arrayOfIDs" back-img="img/service-sliders/{{someID}}/1.jpg"></div>

Since you mentioned ng-src and it seems as though you want the page to finish rendering before loading your image, you may modify jaime's answer to run the native directive after the browser finishes rendering.

This blog post explains this pretty well; essentially, you insert the $timeout wrapper for window.setTimeout before the callback function wherein you make those modifications to the CSS.


I've found with 1.5 components that abstracting the styling from the DOM to work best in my async situation.

Example:

<div ng-style="{ 'background': $ctrl.backgroundUrl }"></div>

And in the controller, something likes this:

this.$onChanges = onChanges;

function onChanges(changes) {
    if (changes.value.currentValue){
        $ctrl.backgroundUrl = setBackgroundUrl(changes.value.currentValue);
    }
}

function setBackgroundUrl(value){
    return 'url(' + value.imgUrl + ')';
}

@jaime your answer is fine. But if your page has the $http.get then you have use ng-if

app.directive('backImg', function(){
    return function($scope, $element, $attrs){
        var url = $attrs.backImg;
        $element.css({
            'background-image': 'url(' + url + ')',
            'background-size': 'cover'
        });
    }
});

in the factory

app.factory('dataFactory', function($http){
    var factory = {};
    factory.getAboutData = function(){
        return $http.get("api/about-data.json");
    };
    return factory;
});

in the controller area

app.controller('aboutCtrl', function($scope, $http, dataFactory){
    $scope.aboutData = [];
    dataFactory.getAboutData().then(function(response){
        // get full home data
        $scope.aboutData = response.data;
        // banner data
        $scope.banner = {
            "image": $scope.aboutData.bannerImage,
            "text": $scope.aboutData.bannerText
        };
    });
});

and the view if you use the ng-if like below then you will get the image by interpolation otherwise, you can't get the image because directive get the value before HTTP request.

<div class="stat-banner"  ng-if="banner.image" background-image-directive="{{banner.image}}">

This one works for me

<li ng-style="{'background-image':'url(/static/'+imgURL+')'}">...</li>

It's also possible to do something like this with ng-style:

ng-style="image_path != '' && {'background-image':'url('+image_path+')'}"

which would not attempt to fetch a non-existing image.


just a matter of taste but if you prefer accessing the variable or function directly like this:

<div id="playlist-icon" back-img="playlist.icon">

instead of interpolating like this:

<div id="playlist-icon" back-img="{{playlist.icon}}">

then you can define the directive a bit differently with scope.$watch which will do $parse on the attribute

angular.module('myApp', [])
.directive('bgImage', function(){

    return function(scope, element, attrs) {
        scope.$watch(attrs.bgImage, function(value) {
            element.css({
                'background-image': 'url(' + value +')',
                'background-size' : 'cover'
            });
        });
    };
})

there is more background on this here: AngularJS : Difference between the $observe and $watch methods


Similar to hooblei's answer, just with interpolation:

<li ng-style="{'background-image': 'url({{ image.source }})'}">...</li>