Want to manipulate style's display. Here is the template:
<div style="display: none" #myDiv />
Thought there are 2 ways to do it:
directly
if (1===1) this.myDiv.style.display = "block";
via #ViewChild
#ViewChild('myDiv', { static: false}) myDiv
if (1===1) this.myDiv.style.display = "block";
none working.
You can use ElementRef for this as follows.
HTML
<div class="my-div" style="display: none" />
TS
export class MyComponent implements AfterViewInit {
myDiv;
constructor(private elementRef:ElementRef) {}
ngAfterViewInit() {
this.myDiv = this.elementRef.nativeElement.querySelector('.my-div');
}
}
Then you can change styles using myDiv variable as follows.
this.myDiv.style.display = 'block';
StackBlitz Demo.
Use ngStyle:
<div [ngStyle]="{'display': flag ? 'block' : 'none'}">
...
</div>
where flag can correspond to any boolean variable based on your logic in the corresponding .ts file.
You can use Renderer2 to set style as well, The prototype of setStyle is as following:
setStyle(el: any, style: string, value: any, flags?: RendererStyleFlags2): void
Parameters:
el: any, The element for whcih you set the style.
style: string, The name of the style.
value: any, The new value for style.
flags RendererStyleFlags2, Flags for style variations. No flags are set by default.
So you have to avoid use of ElementRef because direct access to the dom is not good for security, it is not safe, You can instead use Renderer2 to set Style
Demo example:
https://stackblitz.com/edit/renderer2-example-2-oryw2m?file=src/app/app.component.ts
Code Example:
import { Component, Renderer2, AfterViewInit, ViewChild, ElementRef } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent implements AfterViewInit {
#ViewChild('test') test: ElementRef;
constructor(private renderer: Renderer2) {}
ngAfterViewInit() {
this.renderer.setStyle(this.test.nativeElement, 'backgroundColor', 'red');
this.renderer.setStyle(this.test.nativeElement, 'color', 'white');
}
}
Related
Im trying to create a directive with two selectors in Angular 8. The logic is common between the two types of selectors except for one of the selectors should add an additional css class.
Heres what I'm attempting to do. To Call
<my-label type='error'>foo</my-label>
<my-label-circle type='error'>bar</my-label-circle>
I would like to reuse the my-label directive since the circle is just another css styling on it. Though i would not like to add it to my types input.
#Directive({
selector: '[my-label], [my-label-circle]'
});
class MyLabel {
#Input() type: LabelType = Default;
}
The HostBinding function on class will then just use the input to construct the element. How would I extend that functionality to also use the selector that was given. So for example on the HostBinding function would look like
if(selector == 'my-label-circle')
return 'label label-circle ${type}';
else
return 'label ${type}';
How do I get access to the selector used. Or is there a better way to look at it.
You can add one more optional Input parameter Shape and with the help of Renderer2 Service can add any css.
import { Directive, Renderer, ElementRef } from '#angular/core';
#Directive({
selector: '[my-label]'
})
export class ExploreRendererDirective {
private nativeElement : Node;
#Input() shape: 'default'| 'circle' = 'default';
#Input() type: LabelType = Default;
constructor( private renderer : Renderer2, private element : ElementRef )
{
this.nativeElement = element.nativeElement;
if (this.shape === 'circle') {
// Add css classes for circle.
this.renderer.setAttribute(nativeElement, 'class', 'your-class-here');
}
}
}
// for default
<label my-label type='error'>foo</label>
// for circle
<label my-label type='error' [shape]="'circle'">foo</label>
Or the second solution is two create two directives .
import { Directive, Renderer, ElementRef } from '#angular/core';
#Directive({
selector: '[my-label]'
})
export class ExploreRendererDirective {
private nativeElement : Node;
#Input() type: LabelType = Default;
constructor( private renderer : Renderer2, private element : ElementRef
)
{
this.nativeElement = element.nativeElement;
}
}
import { Directive, Renderer, ElementRef } from '#angular/core';
#Directive({
selector: '[label-circle]'
})
export class ExploreRendererDirective {
private nativeElement : Node;
#Input() type: LabelType = Default;
constructor( private renderer : Renderer2, private element : ElementRef
)
{
this.nativeElement = element.nativeElement;
// Add css classes for circle.
this.renderer.setAttribute(nativeElement, 'class', 'your-class-here');
}
}
// for default
<label my-label type='error' label-circle>foo</label>
Also you can use inheritance to enhance the solution.
Hope It will help you !!
I am using the following code for creating the dynamic components
import {
Component, OnInit, ViewContainerRef, ViewChild, ViewChildren,
ReflectiveInjector, ComponentFactoryResolver, ViewEncapsulation, QueryList, Input, AfterViewInit
} from '#angular/core';
import { Router, ActivatedRoute } from '#angular/router';
import { forEach } from '#angular/router/src/utils/collection';
import { IComponent } from 'app/app.icomponent';
#Component({
encapsulation: ViewEncapsulation.None,
selector: 'dynamic-component',
entryComponents: [HomeComponent, HighlevelSignalComponent],
template: `
<div #dynamicDiv [ngClass]="classFromMenu" >
<ng-template #dynamicComponentContainer></ng-template>
</div>
`,
styleUrls: [
'./dynamic-content.component.css'
],
})
export class DynamicComponent implements IComponent, OnInit, AfterViewInit {
classFromMenu: any;
#ViewChild('dynamicComponentContainer', { read: ViewContainerRef }) dynamicComponentContainer: ViewContainerRef;
constructor(private resolver: ComponentFactoryResolver, private route: Router,
private activatedRoute: ActivatedRoute, ) {
}
.......
buildComponent(passedData) {
// orderAndObjs has the data for creating the component
this.orderAndObjs.forEach(obj => {
var componentFactory = this.resolver.resolveComponentFactory(obj.component);
var compRef = this.dynamicComponentContainer.createComponent(componentFactory);
// compRef is the component that is created.
//Assuming the component that i am trying to create is <dynamic-component>.
//I want to add either a class or any other attribute like this
//<dynamic-component class="flex">
});
}
}
}
The dynamic-component is created perfectly fine and everything is working as expected. But the only issue is I want to add a class for dynamic-component so that it can be
<dynamic-component class="dynamicClass">
Any help is appreciated :(
Hmm.. I usually add it to the selector of component that is supposed to be an entryComponent ...
selector: 'dynamic-component.someclass',
^^^^^^^^^^^
to add attribute use attribute selector:
selector: 'dynamic-component[myattr=value]',
I call it hidden feature of entryComponents
but its declarative approach and can't be changed at runtime(indeed we can change it)
In Angular 5/6, using Renderer2 from #angular/core, you can do something like below:
constructor(private resolver: ComponentFactoryResolver, private route: Router,
private activatedRoute: ActivatedRoute, private renderer2: Renderer2) {
}
buildComponent(passedData) {
this.orderAndObjs.forEach(obj => {
var componentFactory = this.resolver.resolveComponentFactory(obj.component);
var compRef = this.dynamicComponentContainer.createComponent(componentFactory);
this.renderer2.addClass(compRef.location.nativeElement, 'flex');
});
}
High-level DOM operations are performed with Renderer2 provider. Considering that it was injected, it is:
this.renderer2.addClass(compRef.location.nativeElement, 'dynamicClass');
It should be noticed that depending on how dynamic element is attached to DOM, this may be unnecessary complication.
Considering that dynamicComponentContainer is real DOM element and not <ng-template>, the view of dynamic component can be directly mounted to the container, thus eliminating <dynamic-component> wrapper element:
Given the container:
<div class="dynamicClass" #dynamicComponentContainer></div>
It will be:
var compRef = componentFactory.create(
this.injector,
[],
this.dynamicComponentContainer.element.nativeElement
);
I have pipe:
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'spaning',
pure: false
})
export class SpanPipe implements PipeTransform
{
transform(value: string): string
{
return "<span class='mark'>xxx</div>"+value;
}
}
And use it like this:
<div [innerHTML]="movie.title| spaning"></div>
How to style .mark class in css? I want that xxx become red. I do not interested in workaround, class must be added in pipe, as above.
Answer is somehow related to
Angular 2 - innerHTML styling, but I can't find solution by myself.
If I just add style to my component where I use this pipe:
.mark{
color: red;
}
I get:
"WARNING: sanitizing HTML stripped some content (see http://g.co/ng/security#xss)."
[innerHTML] can not be used without DOMSanitizer provider or it will throw security error. You can use DOMSanitizer provider in your custom pipe to sanitize your HTML as shown below,
import { Pipe, PipeTransform } from '#angular/core';
import { DomSanitizer, SafeHtml } from '#angular/platform-browser'
#Pipe({
name: 'spaning',
pure: false
})
export class SpanPipe implements PipeTransform
{
constructor(private sanitized: DomSanitizer) {}
transform(value: string,color:any): SafeHtml{
return this.sanitized.bypassSecurityTrustHtml("<span class='mark' [ngStyle]="{'color':color}">xxx</div>"+value);
}
}
HTML
<div [innerHTML]="movie.title| spaning :'red'"></div>
https://plnkr.co/edit/p0hsn57WT9FfO6E6lRjL?p=info <- plunkr
Turn the view encapsulation mode for your component to 'None' for the hard-coded class to be work in the component
import { ViewEncapsulation } from '#angular/core'
in the decorator
selector: 'your-component',
encapsulation: ViewEncapsulation.None,
then sanitize the HTML in your pipe before returning it
export class SpanPipe implements PipeTransform
{
constructor(private sanitized: DomSanitizer) {}
transform(value: string): any {
return this.sanitized.bypassSecurityTrustHtml("<span class='mark'>xxx</div>"+value);
}
}
EDIT:
Sorry for that.. When you're inserting new html tags you must use DOMSanitizer.
I'm attaching plunker to show how to use it properly
https://plnkr.co/edit/vBnF9hPSpw46053FQ08G?p=preview
You can use ngStyle.
Pipes transform function get two parameters: 'value' and 'args':
export interface PipeTransform {
transform(value: any, ...args: any[]): any;
}
So you can pass your pipe arguments. In this case I'm passing the string 'red' (Witch could easily be a variable..) and use it inside the transform function.
.html:
<div [innerHTML]="movie.title| spaning :'red'"></div>
.ts
import { Pipe, PipeTransform } from '#angular/core';
import { DomSanitizer } from '#angular/platform-browser';
#Pipe({
name: 'spaning',
pure: false
})
export class SpanPipe implements PipeTransform
{
transform(value: string,color:any): string
{
return this.sanitizer.bypassSecurityTrustHtml("<span class='mark' style=color:"+color+">xxx "+value+"</div>");
}
}
I am making a simple hello world app (currently migrating from angular 1) I used angular-cli to generate my components and it created a stylesheet and linked it in my component via styleUrls. None of my styles apply the way I think they are supposed to unless I do "viewEncapsulation.none." Is there something I'm missing? Or is there a better way to write out css for this?
If I use the default encapsulation ( ViewEncapsulation.Emulated or even native) my styles don't show up at all.
import { Component, OnInit } from '#angular/core';
import { ViewEncapsulation } from '#angular/core';
#Component({
selector: 'app-header',
templateUrl: './header.component.html',
encapsulation: ViewEncapsulation.None,
styleUrls: ['./header.component.css']
})
export class HeaderComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
My CSS looks like this:
.app-header{
display: block;
opacity:0.2;
}
You need to use this css:
:host() {
display: block;
opacity:0.2;
}
Don't manipulate the component tag, this tag scope belongs to the parent component who uses it. manipulate only tags inside your html template component.
I think you need to set module:module.id in the component declaration for angular to search for the file in the right location. Have you verified that the CSS gets loaded in the non working case?
I am assuming you are using the app-header css somewhere in header.component.html? Try the below code.
import { Component, OnInit } from '#angular/core';
import { ViewEncapsulation } from '#angular/core';
#Component({
module: module.id,
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.css']
})
export class HeaderComponent implements OnInit {
constructor() { }
ngOnInit() { }
}
I have created a plunker here:
http://plnkr.co/edit/8bwqkYQ6tqrpGwHT588y?p=preview
that shows the issue.
Basically, I have 2 components. The first component has a 2-way binding of a property to the child component.
My parent component is:
import { Component, Input, Output, EventEmitter } from '#angular/core'
import { ChildComponent } from "./childComponent"
#Component({
selector: 'parentComponent',
template: `
<div>
Reset<br>
<div>Parent SelectedId: {{selectedId}}</div>
<childComponent [(selectedId)]="selectedId"></childComponent>
</div>
`,
directives: [ChildComponent]
})
export class ParentComponent {
#Input() selectedId: number;
ngOnChanges(changes) {
console.log("Parent changes called!");
}
}
and my child component:
import { Component, Input, Output, EventEmitter } from '#angular/core'
#Component({
selector: 'childComponent',
template: `
<div>
<div>Child SelectedId: {{selectedId}}</div>
</div>
`,
directives: []
})
export class ChildComponent {
#Input() selectedId: number;
#Output() selectedIdChange: EventEmitter<number> = new EventEmitter<number>();
constructor() {
setTimeout(() => {
this.selectedId = 100;
this.selectedIdChange.emit(this.selectedId);
}, 2000);
}
ngOnChanges(changes) {
console.log("Child changes called!");
}
}
In the child, I set a timeout to change the value of selectedId programmatically after 2 seconds, then emit the value back to the parent.
This all works great, except for one thing... the ngOnChange of the parent is only being called once.
I would think that the parent would very much like to know if the child has changed the value, or else what is the point of 2 way binding??
What am I missing here?
The ngOnChange of the parent will only be called if App's selectedId changes, since that's what ParentComponent's input property is bound to.
If you want the parent to be notified of changes made in the child, bind to the xChange event (where x is the name of the input property) – i.e., break up the property and event bindings:
<childComponent [selectedId]="selectedId" (selectedIdChange)="changed($event)"></childComponent>
changed(newValue) {
console.log('newValue', newValue);
this.selectedId = newValue;
}
Plunker