57

I have two components and one with attribute selector. The child component is,

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


@Component({
    selector: '[app-bar-chart]',
    templateUrl: './bar-chart.component.html',
    styleUrls: ['./bar-chart.component.css']
})
export class BarChartComponent implements OnInit {

    @Input() chartContainer: ElementRef;
    @Input() title: string;
    @Input() chartData: {
        title: string,
        content: {
            label: string,
            value: number,
            fill?: string
        }[]
    }[];

    constructor() { }

    ngOnInit() {
        console.log(this.chartContainer);
        console.log(this.chartContainer.nativeElement);
    }

}

The parent component is,

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

@Component({
    selector: 'app-dashboard',
    template: `<div><div class="graph-image" style="width: 100%; height: 400px;" app-bar-chart [title]="barChartTitle" [chartData]="ChartData" [chartContainer]="chartContainer" #chartContainer></div></div>`,
    styleUrls: ['./dashboard.component.css']
})
export class DashboardComponent implements OnInit {
    barChartTitle = 'Transaction History';
    ChartData = [
        {
            "title": "May2017",
            "content": [
                {
                    "label": "payable",
                    "value": 10
                }
            ]
        },
        {
            "title": "Jun2017",
            "content": [
                {
                    "label": "payable",
                    "value": 120
                }
            ]
        }
    ];
    constructor() { }

    ngOnInit() {
    }

}

I am passing the local reference from the parent component to the child component. When I am consoling the native element of this local reference it is 'undefined'. How can I access the native element so that I can access the style width and height of the component div.

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
shafeequemat
  • 1,002
  • 2
  • 17
  • 22

4 Answers4

80

If you need the ElementRef of a component, you can't use a template variable. If the element is a component, you'll get the component instance instead. A template variable only returns ElementRef for plain HTML elements.

To get ElementRef of a component, you need to use @ViewChild()

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

and then pass it along with

[chartContainer]="myChartContainer"

I would make the input a setter

 private _chartContainer:ElementRef;
 @Input() 
 set chartContainer(value: ElementRef) {
   this._chartContainer = value;
   console.log(this.chartContainer);
   console.log(this.chartContainer.nativeElement);
 }

but ngOnInit works as well Plunker example

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
56

The problem is that if you define a template reference on the element that Angular views as a host element of the component, you will get a reference to the component instance. Here:

<... chartContainer]="chartContainer" #chartContainer></div>

chartContainer will point to the instance of the BarChartComponent and that is why nativeElement is undefined.

To get elementRef of the host element, you don't need any bindings or lifecycle hooks, just inject the element into the constructor:

export class BarChartComponent implements OnInit {
   constructor(element: ElementRef) {
        console.log(element.nativeElement);
   }
Max Koretskyi
  • 101,079
  • 60
  • 333
  • 488
  • 1
    In the case where a parent waits for its childdren ngAfterViewInit is correct. However, when the child receives the parent ngOnInit should be ok. Is it ok? – kimy82 Jul 27 '17 at 07:34
  • @kimy82, I misunderstood the question. I updated the answer – Max Koretskyi Jul 27 '17 at 07:40
  • 1
    This was particularly helpful as it allowed me to access both the component instance and the native `ElementRef` from a single `@ViewChildren` query. – Alex Walker Nov 21 '19 at 14:25
8

As others said, you get the Component's reference when you try to reference it with the @ViewChild('templateId') element; property. But that's totally fine, because we can reference the nativeElement of the child component in its constructor and make it publicly available from the outside components:

  // Child component
  nativeElement: HTMLElement;

  constructor(element: ElementRef) {
    this.nativeElement = element.nativeElement;
  }
  // Parent component:
 
  @ViewChild('childId') element; // element.nativeElement works now

Ambrus Tóth
  • 552
  • 6
  • 14
0

In the first selector you have set '[app-bar-chart]' and it should be without square brachets. To pass the parent you should use:

 [chartContainer]="this"

instead of:

 [chartContainer]="chartContainer"
kimy82
  • 4,069
  • 1
  • 22
  • 25