44

@Component({
    selector: '.donation',
    template: `
    <figure id="donation" move>
        <img src="image/qrcode.png"/>
        <figcaption>
        Buy me a cup of coffee.
        </figcaption>
    </figure>
    `
})
export class DonationComponent{}

@Directive({
    selector: '[move]'
})
export class MoveDirective{}

Hey, I want to get the <figure id="donation"> element's width/height within MoveDirective and DonationComponent. I have read the documentation several times but still cannot find a way to this answer. Does somebody know this? Thanks a lot!

pjpscriv
  • 866
  • 11
  • 20
胡亚雄
  • 2,161
  • 1
  • 19
  • 21

4 Answers4

65

You can use ElementRef as shown below,

DEMO : https://plnkr.co/edit/XZwXEh9PZEEVJpe0BlYq?p=preview check browser's console.

import { Directive, Input, Output, ElementRef, Renderer } from '@angular/core';

@Directive({
  selector:"[move]",
  host:{
    '(click)':"show()"
  }
})

export class GetEleDirective{
  
  constructor(private el:ElementRef) { }

  show(){
    console.log(this.el.nativeElement);
    
    console.log('height---' + this.el.nativeElement.offsetHeight);  //<<<===here
    console.log('width---' + this.el.nativeElement.offsetWidth);    //<<<===here
  }
}

Same way you can use it within component itself wherever you need it.

pjpscriv
  • 866
  • 11
  • 20
micronyks
  • 54,797
  • 15
  • 112
  • 146
  • 4
    Thanks a lot,It works perfect!But I am still a little confused about why using offsetHeight but not the height property to get the element's height? – 胡亚雄 Oct 08 '16 at 02:51
  • 3
    `ElementRef` has been marked as security risk: https://angular.io/docs/js/latest/api/core/index/ElementRef-class.html - is it still ok to use it? – shiva Feb 27 '17 at 14:19
  • @shiva there isn't a risk, since you are also extracting information from the DOM, not inserting dangerous elements. – bersling Jun 12 '17 at 21:12
  • What about the case if children of host element have bigger width or height than the host. How can we get correct width and height of host element? – Sobol Roman Dec 14 '17 at 11:34
  • Can this be set to handle resize events? Or is that really hacky? – Simon_Weaver Jan 26 '18 at 06:53
  • 1
    @Simon_Weaver: Yes you can do it or you can use `windows.resize()` event also but google it so you will come to know more about it. – micronyks Jan 26 '18 at 06:57
  • @micronyks looking like ngDoCheck() and testing inside for _ele.nativeElement.clientWidth is one way. Obviously being careful to not trigger an action every time it’s called! Seems to be working for what i need (homemade responsive design). Thx – Simon_Weaver Jan 26 '18 at 07:25
38

For a bit more flexibility than with micronyks answer, you can do it like that:

1. In your template, add #myIdentifier to the element you want to obtain the width from. Example:

<p #myIdentifier>
  my-component works!
</p>

2. In your controller, you can use this with @ViewChild('myIdentifier') to get the width:

import {AfterViewInit, Component, ElementRef, OnInit, ViewChild} from '@angular/core';

@Component({
  selector: 'app-my-component',
  templateUrl: './my-component.component.html',
  styleUrls: ['./my-component.component.scss']
})
export class MyComponentComponent implements AfterViewInit {

  constructor() { }

  ngAfterViewInit() {
    console.log(this.myIdentifier.nativeElement.offsetWidth);
  }

  @ViewChild('myIdentifier')
  myIdentifier: ElementRef;

}

Security

About the security risk with ElementRef, like this, there is none. There would be a risk, if you would modify the DOM using an ElementRef. But here you are only getting DOM Elements so there is no risk. A risky example of using ElementRef would be: this.myIdentifier.nativeElement.onclick = someFunctionDefinedBySomeUser;. Like this Angular doesn't get a chance to use its sanitisation mechanisms since someFunctionDefinedBySomeUser is inserted directly into the DOM, skipping the Angular sanitisation.

pjpscriv
  • 866
  • 11
  • 20
bersling
  • 17,851
  • 9
  • 60
  • 74
0

@ViewChild doesn't work for everybody- at least, not for me. I'm using Angular 9. What did work was using @ViewChildren:

@ViewChildren('myIdentifier')
myIdentifier: QueryList<ElementRef>;

ngAfterViewInit() {
  this.myIdentifier.changes.subscribe((identifiers) => {
    console.log("OFFSET WIDTH", identifiers.first.nativeElement.offsetWidth);
  });
}
Wassim Katbey
  • 298
  • 4
  • 9
  • `@ViewChild` works always, when you ask for the element, the element was in the app. If you has under a `*ngIf="condition"` and the condition is not fullfilled, Angular can found it. If your condition is false and makes the condition true but you don't wait Angular "repaint", Angular can not find it -typical you enclosed in a setTimeout() to give time Angular "repaint"- – Eliseo Mar 31 '22 at 16:29
  • @Eliseo ngAfterViewInit is run only once, if I use *ngIf to see if myIdentifier returns a truthy value, it will just return false, won't it? Should I place this condition in another lifecycle hook? – Wassim Katbey Mar 31 '22 at 17:01
  • Lybelle, yes ngAfterViewInit is run only one. But ViewChild "check" if exist or not unless you use `@ViewChild('...',{static:true})` -static true, makes Angular don't "check" the element-. But if you use `@ViewChild()` without "static:true" Angular "check if exist or not" the element (imagine like a `@Input` that can have two values "true/false"). So you can make some like: `click(){this.condition=true;setTimeout(()=>{console.log(this.element.nativeElement.offsetWidth})`. a little example in this [stackblitz](https://stackblitz.com/edit/angular-ivy-mo5bj7?file=src%2Fapp%2Fapp.component.ts) – Eliseo Apr 01 '22 at 06:45
0
const rowHeight = this.elRef.nativeElement.querySelector('.datagrid-table > .datagrid-row').getBoundingClientRect().height;

https://angular.io/guide/migration-renderer

Inspired by this getting 'nativeElement.querySelector is not a function' exception in Angular2

rofrol
  • 14,438
  • 7
  • 79
  • 77