Attribute property binding for background-image url in Angular 2 - css

I have been struggling to figure out the best way to dynamically change the background-image attribute in a number of Angular 2 components.
In the following example, I am attempting to set the background-image of a div to an #Input value using [ngStyle] directive:
import {Component, Input} from '#angular/core';
import { User } from '../models';
// exporting type aliases to enforce better type safety (https://github.com/ngrx/example-app)
export type UserInput = User;
#Component({
selector: 'profile-sidenav',
styles: [ `
.profile-image {
background-repeat: no-repeat;
background-position: 50%;
border-radius: 50%;
width: 100px;
height: 100px;
}
`],
template: `
<div class="profile-image" [ngStyle]="{ 'background-image': url({{image}})">
<h3>{{ username }}</h3>
`
})
export class ProfileSidenav {
#Input() user: UserInput;
blankImage: string = '../assets/.../camera.png';
// utilizing "getters" to keep templates clean in 'dumb' components (https://github.com/ngrx/example-app)
get username() {
return this.user.username;
}
get image() {
if (!this.user.image) { return this.cameraImage;
} else { return this.user.image; }
}
I don't think the issue is with the observable, since username displays and doing something like <img *ngIf="image" src="{{ image }}"> renders the image. I have to access the background-image attribute because apparently that is the best way to make a circular image, but in general would like to know how to do this.
EDIT:
My original [ngStyle] declaration had unnecessary curly brackets (ngStyle is a directive that can take a variable), and was missing string tags around url() and image. The correct way is (as answered below) is:
<div class="profile-image" [ngStyle]="{'background-image': 'url(' + image + ')'}"></div>`.
As stated in the original edit, a solution can also be achieved with the Renderer class in Angular 2. I have yet to do it but think there should be a way with setElementStylesor something like that. I will try to post an example but would love if someone else showed me (and others) how to for the time being.

I think that you should use something like that:
<div class="profile-image"
[ngStyle]="{ 'background-image': 'url(' + image + ')'}">
where image is a property of your component.
See this question:
How to add background-image using ngStyle (angular2)?

You don't need to use NgStyle. You can also do this:
[style.background-image]="'url(' + image + ')'"
See more at How to add background-image using ngStyle (angular2)?

redfox05's answer works well since there is no space in the image URL, but by a bit change in code we can make it work again:
<div style.background-image="url('{{image}}')"></div>"

The main reason is simple, you declared a global variable as blankImage but in the template you called image instead of blankImage.
Your ts code variable blankImage
blankImage: string = '../assets/.../camera.png';
Your template code variable image
<div class="profile-image" [ngStyle]="{'background-image': 'url(' + image + ')'}"></div>

that's wrong
I think you should add:
<div class="profile-image" [ngStyle]="{ 'backgroundImage': url({{image}})">
Regards

Related

How to assign dynamic variables to CSS content in Vue.js?

Apologies if this shows how much of a novice I am, but I'd like to know more about dynamic variables and CSS in Vue. I'd like to create a system where each time a button is pressed, the letters of the button label become further apart.
Inside a component is it possible to use a counter script such as:
<script>
export default {
name: 'Counter',
data() {
return {
count: 3,
}
},
methods: {
intrement() {
this.count += 1;
}
}
}
</script>
And then use the count integer value to change CSS text spacing for example?
So that in the template, I could use:
<template>
<header>
<div>
<button class="header_button" style="letter-spacing: `v-bind(count) + ch`;">MYBUTTON</button>
</div>
</header>
</template>
I appreciate this is a strange and specific example, but if anyone could give me some feedback as to why this doesn't work, as well as suggestions on how I could achieve this I'd be super appreciative.
In that case, you can directly use the following
<button :style="`letter-spacing: ${count}ch;`">
Here is a playground.
PS: :style is a shorthand for v-bind:style as explained here.
v-bind for CSS (mixing script + style) is also a thing.
Here, you're only using script + template combo, so an interpolation is enough.

inline style vs className in Componet context

Hello I try to understand when it's necessary to use inline style instead className in this case. I take a long time to solve my problem of translation. At the beginning I want to translate component by using classNameand that's don't work. it's very weird because in my point of view there is no reason that's happen. So I figure there is something wrong in my code, but what... I have not yet found. So I finish trying to translate by using a inline style. Miracle, that's work fine.
My question is why ?
Work well
export function Content() {
return (
<div style={{transform: 'translateY(100px)'}}>
<Test/>
<Footer />
</div>)
}
don't work
export function Content() {
return (
<div className={container_content}>
<Test/>
<Footer />
</div>
)
}
css
.container_content {
transform: translateY(100px);
}
Nota bene :
The problem is not from the method. To use className in my jsx
must be like that:
import { container_content } from "./test.module.css";
and next
<div className={container_content}><div>
So this part of code is good, the issue seems to come from elsewhere...
What's happening is that when you use the inline style you are passing an object that includes the styling for that component. When you use the className you need to pass in a string for the class you want to use. Right now you are passing a variable name. Either of these works:
<div className={"container_content"}>
OR
<div className="container_content">
If you think about it in regular html you would do
<div class="container_content">
EDIT: Given your updated question, you should just import the css file with:
import "./test.module.css"
and then use the solution I mentioned.
inside the js file, you need to import the CSS file like this
import " css-file-dir";
and then you can Reference to the CSS classes inside your component as a string
example :
className="container_content"

Styling components with inline CSS - Ionic2, AngularJS

I am trying to append an inline CSS rule for background-image using a dynamic variable set inside the constructor like so:
<div style="background-image: url('{{backgroundImage}}');">
...
</div>
then in my Component:
export class SomeComponent {
backgroundImage = '';
constructor(public navCtrl: NavController, public navParams: NavParams) {
this.backgroundImage = 'https://unsplash.it/200/300' ; }
}
However, when the element is rendered to the screen, the inline CSS rule gets omitted.
I tried using Angular's ng-style, but it returns "Can't bind to 'ng-style' since it isn't a known property of 'div'".
I also tried setting styles inside my #Component declaration as noted in this answer, but this wouldn't fly in my case, as I need the backgroundImage variable to be dynamic.
Since Ionic2 (or just Ionic) is built on top of Angular (an not AngularJS), you can do that like this:
<div [ngStyle]="{ background: 'url(' + backgroundImage + ')' }"></div>
or
<div [style.background]="'url(' + backgroundImage + ')'"></div>

Change style of pseudo elements in angular2

Is it possible to change style of pseudo elements using [style] or [ngStyle] in angular2?
in order to get a blur effect on a div acts like an overlay, and I should set up background-image on pseudo element.
I tried something like
<div class="blur" [style.before.backgroundImage]="'url('+ featuredImage[i] + ' )'">
it didn't work. I also tried this
<div class="blur" [ngStyle]="'{:before{ background-image:url('+ featuredImage[i] + ' )}}'">
You can achieve what you need with CSS variables.
In your style sheet you can set the background image like this:
.featured-image:after { content: '';
background-image: var(--featured-image);
}
After that you can programmatically set this variable on the same element or higher up in the DOM tree:
<div class="featured-image" [ngStyle]="{'--featured-image': featuredImage}">
More about CSS variables here: https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_variables Note that the browser support is not complete yet.
Also note that you will need to sanitize the url/style using sanitizer.bypassSecurityTrustResourceUrl(path) or sanitizer.bypassSecurityTrustStyle('--featured-image:url(' + path + ')')):
No it's not possible. It is actually not an Angular issue: pseudo elements are not part of DOM tree, and because of that do not expose any DOM API that can be used to interact with them.
Usual approach if you want to deal with pseudo elements programmatically is indirect: you add/remove/change class and in CSS make this class affect corresponding pseudo-element. So in your case you could have one more class that changes necessary style:
.blur:before {/* some styles */}
.blur.background:before {/* set background */}
Now all you need to do is to toggle .background class on the element when you need before pseudo-element to get a background. You can use NgClass, for example.
if you want to add other properties I did it like this:
<div class="progress" [style]= "'--porcentaje-width:' + widthh " ></div>
and the css:
.progress::after {
content: '';
width: var(--porcentaje-width);
}
this worked for me :)
With current versions of Angular 2+ you can use CSS Variables to achieve this as well as sanitizing your input.
In your style sheet define the rule using CSS Variables. A fallback can also be defined as CSS Variables aren't supported by IE.
https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties
.featured-image:after {
content: '';
// Fallback for IE
background-image: url('fallback-img.png');
background-image: var(--featured-image);
}
Rather than bypassing security trust style, you can also sanitize your input with a reusable pipe:
https://angular.io/api/platform-browser/DomSanitizer#sanitize
import {Pipe, PipeTransform, SecurityContext} from '#angular/core';
import {DomSanitizer, SafeStyle} from '#angular/platform-browser';
#Pipe({
name: 'safeStyle',
})
export class SafeStylePipe implements PipeTransform {
constructor(protected sanitizer: DomSanitizer) {}
transform(value: string): SafeStyle {
if (!value) return '';
return this.sanitizer.sanitize(SecurityContext.STYLE, value);
}
}
In your template:
<div class="featured-image" [style.--featured-image]="featuredImage[i] | safeStyle"></div>

variable background images through angularJS ng-style

Sorry if this is a bit of a newby question. I'm trying to create a login page that has a background image, while the rest of my pages do not. I've used ng-style but I can't get the property to update on page changes.
in my index.html:
<body ng-app="myApp" ng-style="bodyStyle" ng-controller="bodyController">
//content
</body
bodycontroller:
var image="http://momentumbooks.com.au/wp-content/uploads/2013/06/space.jpg";
if ($location.path() === '/login') {
$scope.bodyStyle = {background: "url(" + image + ") no-repeat center center fixed"};
} else{
$scope.bodyStyle={background: ""}
}
Obviously this doesn't work because the function is only called once. I've tried using rootScope, but I can't seem to use rootScope properties in ng-style (and everywhere i look, people are advising against using rootScope for this purpose). How do I create a dynamic background? Also i'd prefer not to use a controller on my body tag, if possible.
update
The main problem i'm having is that the background image does not update when changing paths. The image is set in bodycontroller, and when logging in and changing paths it is not changed.
Per suggestion I could write a service, I've used them before but only through getters and setters, so I assume i can create a service that sends a call to a controller? Looking something like this:
<body ng-app="myApp" ng-style="bodyStyle" ng-controller="bodyController">
//content
</body
bodycontroller
var image=??;
$scope.bodyStyle = {background: "url(" + image + ") no-repeat center
some service
.service('backgroundService', function (image) {
var backgroundImage = image
// somhow set bodycontroller image?
});
and then somehow call the service when route is changed? I haven't found a way to inject services into my router config, which is what I think i would need for that.
So i figured out an easy way to do this with some help.
in app.js add this:
.run(function ($rootScope, $location) {
$rootScope.$on( "$routeChangeStart", function(event, next, current) {
$rootScope.bodyClass = $location.path().replace('/', '') + '-page';
});
});
and change index to:
<body ng-app="myApp" ng-class="bodyClass">
and Css:
.login-page {
background: url("someImage") no-repeat center center fixed;
}
IMO it would be easier to just toggle an ng-class based on location. So you could do something like -
if ($location.path() === '/login') {
$scope.isLogin = true;
} else{
$scope.isLogin = false;
}
then on the html
<body ng-app="myApp" ng-class="{'customBgClass' : isLogin }" ng-controller="bodyController">
Then just set everything you want on that css class
.customBgClass {
background: url("http://momentumbooks.com.au/wp-content/uploads/2013/06/space.jpg") no-repeat ce;ter center fixed;
}

Resources