I am trying to query the DOM and add a style to it in Angular. One would think its simple but the following does not work and gives an error, is there an Angular specific way to do it?
let target = document.querySelector(".dom-element");
target.style.width = "100%";
You can do it using ngStyle: https://angular.io/api/common/NgStyle
[ngStyle]="{width: variableFromController}"
Or using #ViewChild and ngAfterViewInit: https://angular.io/api/core/ViewChild
template:
<div #domElement></div>
component:
#ViewChild('domElement') element: ElementRef;
public ngAfterViewInit() {
this.domElement.nativeElement.style.width = '100%';
}
What you need is the renderer2, you should avoid any direct DOM manipulation. This should do it:
constructor(private renderer: Renderer2) {
#ViewChild("dom-element") target: ElementRef;
ngAfterViewInit() {
// target is set here
this.renderer.setStyle(this.target.nativeElement, 'width', '100%');
}
Related
Im trying to change the style of an external label of a custom element. The classname is attached correctly but style is not defined.
The custom element is created on stencil and react.
elementRef: ElementRef;
constructor(#Inject(ElementRef) elementRef: ElementRef) {
this.elementRef = elementRef;
}
ngAfterViewInit(): void {
const parentElement = document
.querySelector('.mobilePhone')
.shadowRoot.querySelector('label');
parentElement.className = 'newClassName';
}
<custom-form-element class="mobilePhone" >
<label></label>
</custom-form-element>
parentElement.setAttribute('style', 'color:green');
I have a div with the id of 1. I'm trying to set the display to none dynamically. Is there an Angular way of doing this. Currently, I'm using vanilla javascript. I was asking about doing this dynamically because there will be over 60 divs that will be created from an array.
In my html
<div *ngFor="let item of items; i = index;">
<div id={{i}} (click)=hideDiv()></div>
</div>
In my method
hideDiv() {
return document.getElementById('1').style.display = "none";
}
That works but I'm looking for the Angular way of doing the above.
It was suggested that I use #ViewChild. Here's what I've changed. I can't use a Template Reference Variable as the html divs are created dynamically. Unless someone can let me know how to create the template variables dynamically. Although I don't think it's possible to create template variables with a loop.
#ViewChild('imgId', { static: true }) elementRef: ElementRef<HTMLDivElement>;
imgId: string;
Then in the method I have:
this.imgId = event.path[0].attributes[1].value;
this.elementRef.nativeElement.style.display = "none";
The event.path[0].attributes[1].value gets me the id of the image. The imgId shows when I console log it. It's still not changing the display on the div to none. Also I'm getting the error:
Cannot read properties of undefined (reading 'nativeElement')
Yes, you can use the ViewChild query in Angular to do this. In your component, define a query like this:
#ViewChild('#1') elementRef: ElementRef<HTMLDivElement>;
Implement the AfterViewInit interface in your component, and inside it, use this:
this.elementRef.nativeElement.style.display = "none";
You can simply use ngIf for this
Component
shouldDisplay: boolean = true;
hide(): void {
this.shouldDisplay = false;
}
show(): void {
this.shouldDisplay = true;
}
Html
<button (click)="hide()">Hide</button>
<button (click)="show()">Show</button>
<div *ngIf="shouldDisplay">this is the content</div>
Here is the working example
This is the Angular way:
template
<div *ngIf="showMe"></div>
or
<div [hidden]="!showMe"></div>
TypeScript:
showMe: boolean;
hideDiv() {
this.showMe = false;
}
For dynamic items where your don't know how many you will get the best approach would be to add a directive that would store and adjust that for you:
#Directive({ selector: '[hide-me]' })
export class HideDirective {
#Input() id!: string;
#HostBinding('style.display')
shouldShow: string = '';
}
then in your component just address them by ID:
#Component({
selector: 'my-app',
styleUrls: ['./app.component.css'],
template: `
<div *ngFor="let item of items; let index = index;">
<div hide-me id="{{index}}" (click)="hideDiv(index)">Some value</div>
</div>
`,
})
export class AppComponent {
#ViewChildren(HideDirective) hideDirectives!: QueryList<HideDirective>;
items = [null, null, null];
hideDiv(id: number) {
this.hideDirectives.find((p) => p.id === id.toString()).shouldShow = 'none';
}
}
Stackblitz: https://stackblitz.com/edit/angular-ivy-pnrdhv?file=src/app/app.component.ts
An angular official example: https://stackblitz.com/edit/angular-ivy-pnrdhv?file=src/app/app.component.ts
How about passing the div reference to the hideDiv method directly in the Dom using a template variable like this.
<div *ngFor="let item of items; i = index;">
<div #divElement (click)=hideDiv(divElement)></div>
And in your hide div method you will have access to the element directly
hideDiv(div) { div.style.display = "none";}
Here is a Stackblitz example
https://stackblitz.com/edit/angular-ivy-w1s3jl
There are many ways to do this, but in my opinion this is a simple solution the achieves your goal with less code.
PS:
It is always recommended to use the angular Renderer2 to manipulate Dom elements. This service has the method setStyle which you can use for your code.
I want to dynamically create an element and then get its clientWidth. The code snippet looks like this using the regular DOM API.
HTML Snippet:
<div id="non-existent-element"></div>
The element has its css property visibility set to 'hidden'.
Typescript/ Javascript snippet
let labelContainer = document.querySelector('div#non-existent-element');
labelContainer.innerHTML = `
<span>${fundName}</span>
<span> (${performance}%)</span>
`;
let children = labelContainer.children as HTMLCollectionOf<HTMLElement>
for(let i = 0; i < children.length; i++) {
children[i].style.fontSize = fontSize + + 1 +'px';
}
return labelContainer.clientWidth;
How can I achieve the goal using Angular's Element Ref and Renderer2 API?
Simple usage of clientWidth
app.component.html
<p #test>test is the elementRef name</p>
app.component.ts
export class AppComponent implements AfterViewInit {
#ViewChild('test') test: ElementRef;
constructor(private renderer: Renderer2) {
//ERROR TypeError: Cannot read property 'nativeElement' of undefined
// console.log(this.test.nativeElement.clientWidth);
}
ngOnInit() {
//logs: 583
console.log(this.test.nativeElement.clientWidth);
}
ngAfterViewInit() {
this.renderer.setStyle(this.test.nativeElement, 'backgroundColor', 'red');
this.renderer.setStyle(this.test.nativeElement, 'color', 'white');
this.renderer.setStyle(this.test.nativeElement, 'width', '500px');
//logs: 500
console.log(this.test.nativeElement.clientWidth);
}
}
Is there a way to change the OverlayContainer?
I have created a tooltip component, but sometimes I want to attach the overlay to a specific element (by default the overlay is attached to the document body).
Here is how I am creating the overlay:
private initOverlay(): void {
const positionStrategy = this.overlayPositionBuilder
.flexibleConnectedTo(this.elementRef)
.withPositions([this.resolvedConfig]);
this.overlayRef = this.overlay.create({positionStrategy});
}
And this is how I am attaching a template to it:
show(): void {
this.overlayRef.attach(new TemplatePortal(this.tpl, this.viewContainerRef));
}
Please reference this stackblitz example.
looks like you can accomplish this by extending the
OverlayContainer class via the following in app.module.ts
{ provide: OverlayContainer, useFactory: () => new AppOverlayContainer() }
Stackblitz
https://stackblitz.com/edit/angular-material2-issue-ansnt5?file=app%2Fapp.module.ts
This GitHub comment also provides an example of how to package this in a directive
GitHub comment
https://github.com/angular/material2/issues/7349#issuecomment-337513040
Revision 3/22/19 working directive example
Extend the OverlayContainer class via cdk-overlay-container.ts
Stub the class in app.module.ts
providers: [
{ provide: OverlayContainer, useClass: CdkOverlayContainer },
]
In your cdk-overlay-container.ts you are preventing the default _createContainer() from working, and providing your own custom public method myCreateContainer to replace it.
You are essentially creating an empty div here, adding a custom class to it my-custom-overlay-container-class and appending it to the
div the directive is attached to, then passing that container to the
private variable _containerElement in the true OverlayContainer
class.
/**
* Create overlay container and append to ElementRef from directive
*/
public myCreateContainer(element: HTMLElement): void {
let container = document.createElement('div');
container.classList.add('my-custom-overlay-container-class');
element.appendChild(container);
this._containerElement = container;
}
/**
* Prevent creation of the HTML element, use custom method above
*/
protected _createContainer(): void {
return;
}
Then in your cdk-overlay-container.directive.ts your are calling myCreateContainer() and passing the ElementRef as an argument.
this.cdkOverlayContainer['myCreateContainer'](this.elementReference.nativeElement);
Then in your HTML assign the directive where you want it to show up.
<div myCdkOverlayContainer
Stackblitz
https://stackblitz.com/edit/angular-material2-issue-6nzwws?embed=1&file=app/app.component.html
At the moment, I'm using ELEMENTREF to access the DOM through the Redenrer2. Here's a basic example:
import { Directive, Renderer2, ElementRef } from '#angular/core';
#Directive({
selector: '[appHighlight]'
})
export class HighlightDirective {
constructor(private renderer: Renderer2, private el: ElementRef) {
this.renderer.setStyle(this.el.nativeElement, 'background', 'yellow');
}
}
The above code was just a test to leave the syntax highlight with the yellow color.
However, I need to know how do I access the previous element and capture the width of it to move to a left-style property?
Ex: Former brother has the width of 400px. The current element has the left of 400px.
I'm counting on the collaboration.
You can get the parent element:
let parent = this.renderer.parentNode(this.elementRef.nativeElement);
and then select his child somehow, maybe give them incrementing ID's:
let sibling = parent.querySelector('#child5');
and then you have siblings width:
let width = sibling.offsetWidth;.
Hope that helps.