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);
}
}
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 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%');
}
Application is built on angular. In a component, there is a div inside which there is a text. <div>abcdefghijklmnop<div>
Based on the screen size, it should show completely or it should clipped. For this I have found that there is a property 'text-overflow', which clipped the text like abcde.... But the requirement is we have to clip the text in other way,
<first 3 character>...<last 3 character>
So it should look like abc...nop. How can we achieve this? Browser is chrome.
You could use a attribute directive like bellow to do this. For more details please refer Angular DOC
Please Note: You could improve this directive as you wish. This directive is only giving you the basis to implement that feature.
Directive:
import { Directive, ElementRef, OnInit } from '#angular/core';
#Directive({
selector: '[appClip]'
})
export class ClipDirective implements OnInit {
constructor(private el: ElementRef) { }
ngOnInit(): void {
let text: string = this.el.nativeElement.innerHTML;
if(text.length > 6) {
const first3 = text.slice(0, 3);
const last3 = text.slice(text.length - 3)
this.el.nativeElement.innerHTML = `${first3}...${last3}`;
}
}
}
HTML:
<div appClip="">abcdefghijklmnop</div>
Working DEMO
We are trying to change CSS id's based on time. The point is that currently, it manipulates the body. How can we change it into section manipulation?
Angular part
ngOnInit() {
this.run(1000, 10)
}
run(interval, frames) {
var int = 1;
function func() {
document.body.id = "b"+int;
int++;
if(int === frames) { int = 1; }
}
var swap = window.setInterval(func, interval);
}
HTML
<section class='full-screen'>
...
...
</section>
there are different css snippets for #b1, #b2, #b3... since above code changes these ids during each time period. I assume something should be changed here:
document.body.id = "b"+int;
How move that function usage from body into above HTML section?
Add a Template reference variable in your template for the section tag:
<section #section class='full-screen'>
...
...
</section>
Add a #ViewChild decoratored variable in your component's ts file to get this element:
#ViewChild('section', { read: ElementRef }) mySection: ElementRef;
Now you can use it like this in your component's ts file:
ngOnInit() {
this.run(1000, 10)
}
run(interval, frames) {
var int = 1;
function func() {
this.mySection.nativeElement.id = "b"+int;
int++;
if(int === frames) { int = 1; }
}
var swap = window.setInterval(func.bind(this), interval);
}
See this simple DEMO
UPDATE:
Note that you're using function func(), this will cause you a scoping problem with using this as your component object. One way to fix this is by using bind function:
var swap = window.setInterval(func.bind(this), interval);
Updated the demo to show this in action.
document.getElementById("div_top1").setAttribute("id", "div_top2");
You can use this to change section id.
You can do it thanks to angular viewChild feature
in your html:
<div #foo class="myClass"></div>
in your component ts file
import { Component, ViewChild, ElementRef, AfterViewInit } from '#angular/core';
// ....
export MyComponernt implement AfterViewInit {
// ....
#ViewChild('foo') foo: ElementRef;
// ....
ngAfterViewInit(): void {
// this is how you manipulate element id properly thanks to angular viewChild feature
this.foo.nativeElement.id = 'something';
}
// ....
}