63

I want to access the DOM of a component using ViewChild. But when I try to access the nativeElement property, it's undefined.

Below is the snippet.

import { Component, ViewChild, AfterViewInit } from '@angular/core';
import { AlertComponent } from './alert.component';

@Component({
    selector: 'app-root',
    template: `
    <app-alert #alert>My alert</app-alert>
      <button (click)="showAlert()">Show Alert</button>`
})
export class AppComponent implements AfterViewInit {
  @ViewChild('alert') alert;

  showAlert() {
    console.log('alert (showalert)', this.alert.nativeElement);
    this.alert.show();
  }

  ngAfterViewInit() {
    console.log('alert (afterviewinit)', this.alert.nativeElement);
  }
}

Please take a look at the plunk.

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
karthikaruna
  • 3,042
  • 7
  • 26
  • 37

1 Answers1

153

If you want to get a reference of an element that hosts a component or directive you need to specify that you want the element instead of the component or directive

@ViewChild('alert', { read: ElementRef }) alert:ElementRef;

See also angular 2 / typescript : get hold of an element in the template

In your case I guess you need two different @ViewChild() one for the component reference to be able to access the show() method, and a 2nd one to be able to access DOM attributes.

Plunker example

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • Thanks! By default it returns the component instance. How can I explicitly specify the `read` property to return the component instance? Also, it would be great if you can tell what are the other values I can set for different purposes. – karthikaruna Aug 28 '17 at 15:08
  • 3
    If it's a plain HTML element, the default is `ElementRef`, it it hosts a component or directive, you'll get the component instance by default. You can just use the type name of the component or directive to request a specific one like `@ViewChild('alert', { read: AlertComponent }) alert: AlertComponent;` – Günter Zöchbauer Aug 28 '17 at 15:11
  • 4
    Works for `ViewChildren` too – Lahiru Chandima Jul 06 '19 at 13:44
  • 17
    In angular 8, you have to set the static option (which is now mandatory) to false. `@ViewChild('alert', { read: ElementRef, static:false })` Otherwise, it won't work. – alchi baucha Feb 12 '20 at 02:14
  • 5
    Actually `{static: false}` is the default. It's the `{read: ElementRef}` that is now required to get it working. Seems like a massively breaking change to have no mention of it whatsoever in the documentation. – Neutrino Nov 30 '21 at 12:11
  • @Neutrino static has changed in v9 as far as I remember, but read is quite old v3 or v4 I guess. You need `read` if you do not want the default type. For example if it's an Angular component, you'll get the component instead of the ElementRef by default. – Günter Zöchbauer Nov 30 '21 at 12:56
  • How do I get both the `ElementRef` and the `AlertComponent` at once? In my case I have many of them so I'm using `ViewChildren` instead, and with this answer I'd have to call `ViewChildren` twice, but I don't want to assume that the order they get is perfectly synchronized all the time. – Pedro A Mar 23 '22 at 04:12
  • Figured it out: https://stackoverflow.com/a/58453475/4135063 – Pedro A Mar 23 '22 at 05:06
  • @PedroA Did you resolved `ViewChildren` looking for same use case. – sankar muniyappa Jul 07 '22 at 18:35
  • @sankarmuniyappa What I did is what I found here https://stackoverflow.com/a/58453475/4135063 – Pedro A Jul 07 '22 at 18:50