12

I am using *ngFor to create a bunch of divs. I would like to get my hands on one of them an get its width.

.html
<div #elReference *ngFor="let el of elements> HEHE </div>

.ts
  @ViewChild("elReference") elReference: ElementRef;

Apparently, you cannot use ViewChild with *ngFor, because elReference remains undefined.

How do you get element reference created with *ngFor?

sanjihan
  • 4,558
  • 9
  • 44
  • 102
  • Take a look at this thread: https://stackoverflow.com/questions/40165294/access-multiple-viewchildren-using-viewchild – Christian Benseler Oct 31 '17 at 11:18
  • Thanks. I tried @ViewChildren("elReference") elReferences: QueryList; , but still getting an undefined value for elReferences. – sanjihan Oct 31 '17 at 11:38
  • I used it in onInit(). it appears ngAfterViewInit is mandatory. – sanjihan Oct 31 '17 at 11:45
  • 1
    You have to use ngAfterViewInit because you must wait the template to be parsed and the DOM mounted. – Christian Benseler Oct 31 '17 at 11:49
  • Thanks. I am indeed getting the clientWidth now. For some reason, it is rounded to nearest integer. This code console.log(this. elReference.nativeElement.clientWidth); returns 25, although the actual width in browser is 25.11. $("element").outerwidth() also returns 25.11 – sanjihan Oct 31 '17 at 12:07

2 Answers2

10

There is no way to add a template variable selectively, but you can add a marker selectively and then filter by that:

<div #elReference [attr.foo]="el.x == 'foo' ? true : null" *ngFor="let el of elements> HEHE </div>
@ViewChildren("elReference") elReference: QueryList<ElementRef>;

ngAfterViewInit() {
  console.log(this.elReference.toArray()
      .filter(r => r.nativeElement.hasAttribute('foo')));
}
Mohammad Kermani
  • 4,713
  • 6
  • 35
  • 55
Günter Zöchbauer
  • 558,509
  • 191
  • 1,911
  • 1,506
4

There is a more complicated, but more correct way with a directive.

<!-- app.component.html -->
<div *ngFor="let el of elements" [id]="el.id"> HEHE </div>
// div-id.directive.ts
import { Directive, Input, ElementRef } from '@angular/core';

@Directive({
  selector: 'div[id]',
})
export class DivIdDirective {
  @Input() id: number;

  constructor(ref: ElementRef<HTMLDivElement>) {
    this.el = ref.nativeElement;
  }

  el: HTMLDivElement;
}

// app.component.ts
export class AppComponent implements AfterViewInit {
  // ...
  @ViewChildren(DivIdDirective) els: QueryList<DivIdDirective>;

  ngAfterViewInit(): void {
    console.log(this.els.map(({id, el}) => ({id, el}));
  }
}

Eugene P.
  • 909
  • 8
  • 13