angularjs: ng-src equivalente para background-image: url (...)

150

En angularjs tiene la etiqueta ng-src que tiene el propósito de que no recibirá un error por una URL no válida antes de que angularjs pueda evaluar las variables ubicadas entre {{y }}.

El problema es que uso bastantes DIV con un background-imageconjunto en una url. Hago esto debido a la excelente propiedad CSS3 background-sizeque recorta la imagen al tamaño exacto del DIV.

El único problema es que recibo muchos errores por la misma razón por la que crearon una etiqueta ng-src: tengo algunas variables en la URL y el navegador cree que la imagen no existe.

Me doy cuenta de que existe la posibilidad de escribir un crudo {{"style='background-image:url(myVariableUrl)'"}}, pero esto parece "sucio".

He buscado mucho y no puedo encontrar la forma correcta de hacerlo. Mi aplicación se está volviendo un desastre debido a todos estos errores.

Jasper Schulte
fuente

Respuestas:

200

ngSrces una directiva nativa, por lo que parece que quieres una directiva similar que modifique el background-imageestilo de tu div .

Puede escribir su propia directiva que haga exactamente lo que quiere. Por ejemplo

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

Que invocarías así

<div back-img="<some-image-url>" ></div>

JSFiddle con gatos lindos como bonificación: http://jsfiddle.net/jaimem/aSjwk/1/

jaime
fuente
67
Muy buena directiva; El único problema es que no admite interpolación. Probablemente lo modificaría así: jsfiddle.net/BinaryMuse/aSjwk/2
Michelle Tilley
1
Gracias, no importa que sea sin interpolación, ¡tiene gatitos!
Visgean Skeloru
¿Algún problema de seguridad con esto si está cargando desde fuentes no confiables?
Aakil Fernandes
esto funciona, pero solo si la url de su imagen ya está definida, use la respuesta @mythz stackoverflow.com/a/15537359/1479680 para tener una mejor url que se pueda observar
Magico
229

Esta funciona para mí

<li ng-style="{'background-image':'url(/static/'+imgURL+')'}">...</li>
hooblei
fuente
22
Creo que esta debería ser la respuesta aceptada. No es necesario agregar una directiva personalizada.
Jakob Løkke Madsen
2
Agregué
1
Es más sucio, en el sentido de que una directiva se adapta a todos, donde sea que necesite un fondo dinámico. La pregunta mencionó que se sentía menos reutilizable.
Christian Bonato
No funciona en Angular 1.5.8, aún intenta cargar la imagen con imgURLno establecida y imgURLvuelve a intentarlo cuando se configura.
leroydev
Esto es incorrecto, si tiene variables especiales como @% en su URL, se romperá.
efwjames
69

La respuesta anterior no admite interpolación observable (y me costó mucho tiempo tratar de depurar). El enlace jsFiddle en el comentario de @BrandonTilley fue la respuesta que funcionó para mí, que volveré a publicar aquí para su preservación:

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

Ejemplo usando controlador y plantilla

Controlador :

$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];

Modelo :

Usar en plantilla así:

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

o así:

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

También es posible hacer algo como esto con ng-style:

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

que no intentaría obtener una imagen no existente.

principio holográfico
fuente
2
@sqren también vea esto si está utilizando una versión más nueva de Angular (1.1.5 y superior): stackoverflow.com/a/12151492/1218080 . Se agregó soporte para un operador ternario real en vistas.
principio holográfico
La concatenación de cadenas simple como esta me funcionó en el objeto de alcance, en lugar de la interpolación.
Shardul
25

Similar a la respuesta de hooblei, solo con interpolación:

<li ng-style="{'background-image': 'url({{ image.source }})'}">...</li>
wiherek
fuente
1
No funcionó aquí: <span ng-style="{'background-image': 'url(/styles/images/profile.png)'}" style="background-image: url("");"></span>. El enfoque @hooblei funcionó.
Marcos Lima
Obteniendo el mismo resultado :(
Kushal Jayswal
9

solo es cuestión de gustos, pero si prefiere acceder a la variable o función directamente de esta manera:

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

en lugar de interpolar así:

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

entonces puedes definir la directiva un poco diferente con scope.$watchlo que harás $parseen el atributo

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

Hay más antecedentes sobre esto aquí: AngularJS: diferencia entre los métodos $ observe y $ watch

kristok
fuente
2

@jaime tu respuesta está bien. Pero si su página tiene el $ http.get, entonces debe usar ng-if

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

en la fábrica

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

en el área del controlador

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

y la vista si usa el ng-if como se muestra a continuación, obtendrá la imagen por interpolación; de lo contrario, no puede obtener la imagen porque la directiva obtiene el valor antes de la solicitud HTTP.

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

He encontrado con 1.5 componentes que abstraen el estilo del DOM para que funcione mejor en mi situación asíncrona.

Ejemplo:

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

Y en el controlador, algo le gusta esto:

this.$onChanges = onChanges;

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

function setBackgroundUrl(value){
    return 'url(' + value.imgUrl + ')';
}
Pat Migliaccio
fuente
0

Como mencionó ng-srcy parece que desea que la página termine de renderizarse antes de cargar su imagen, puede modificar la respuesta de jaime para ejecutar la directiva nativa después de que el navegador finalice el renderizado.

Esta publicación de blog explica esto bastante bien; en esencia, se inserta el $timeoutenvoltorio para window.setTimeoutantes de la función de devolución de llamada que hacer esas modificaciones en el CSS.

aralar
fuente