726

Does anybody know how to get hold of an element defined in a component template? Polymer makes it really easy with the $ and $$.

I was just wondering how to go about it in Angular.

Take the example from the tutorial:

import {Component} from '@angular/core';

@Component({
    selector:'display',
    template:`
     <input #myname (input)="updateName(myname.value)"/>
     <p>My name : {{myName}}</p>
     `   
})
export class DisplayComponent {
    myName: string = "Aman";
    updateName(input: String) {
        this.myName = input;
    }
}

How do I catch hold or get a reference of the p or input element from within the class definition?

Hitech Hitesh
  • 1,641
  • 1
  • 9
  • 18
Aman Gupta
  • 7,883
  • 5
  • 16
  • 24

15 Answers15

1225

Instead of injecting ElementRef and using querySelector or similar from there, a declarative way can be used instead to access elements in the view directly:

<input #myname>
@ViewChild('myname') input; 

element

ngAfterViewInit() {
  console.log(this.input.nativeElement.value);
}

StackBlitz example

  • @ViewChild() supports directive or component type as parameter, or the name (string) of a template variable.
  • @ViewChildren() also supports a list of names as comma separated list (currently no spaces allowed @ViewChildren('var1,var2,var3')).
  • @ContentChild() and @ContentChildren() do the same but in the light DOM (<ng-content> projected elements).

descendants

@ContentChildren() is the only one that allows you to also query for descendants

@ContentChildren(SomeTypeOrVarName, {descendants: true}) someField; 
`{descendants: true}` should be the default but is not in 2.0.0 final and it's [considered a bug](https://github.com/angular/angular/issues/11645#issuecomment-247650618) This was fixed in 2.0.1

read

If there are a component and directives the read parameter allows you to specify which instance should be returned.

For example ViewContainerRef that is required by dynamically created components instead of the default ElementRef

@ViewChild('myname', { read: ViewContainerRef }) target;

subscribe changes

Even though view children are only set when ngAfterViewInit() is called and content children are only set when ngAfterContentInit() is called, if you want to subscribe to changes of the query result, it should be done in ngOnInit()

https://github.com/angular/angular/issues/9689#issuecomment-229247134

@ViewChildren(SomeType) viewChildren;
@ContentChildren(SomeType) contentChildren;

ngOnInit() {
  this.viewChildren.changes.subscribe(changes => console.log(changes));
  this.contentChildren.changes.subscribe(changes => console.log(changes));
}

direct DOM access

can only query DOM elements, but not components or directive instances:

export class MyComponent {
  constructor(private elRef:ElementRef) {}
  ngAfterViewInit() {
    var div = this.elRef.nativeElement.querySelector('div');
    console.log(div);
  }

  // for transcluded content
  ngAfterContentInit() {
    var div = this.elRef.nativeElement.querySelector('div');
    console.log(div);
  }
}

get arbitrary projected content

See Access transcluded content

iconoclast
  • 21,213
  • 15
  • 102
  • 138
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • 14
    The angular teams advised against using ElementRef, this is the better solution. – Honorable Chow Mar 30 '16 at 14:32
  • 9
    Actually `input` also is an `ElementRef`, but you get the reference to the element you actually want, instead of querying it from the host `ElementRef`. – Günter Zöchbauer Mar 30 '16 at 14:35
  • 47
    Actually using `ElementRef` is just fine. Also using `ElementRef.nativeElement` with `Renderer` is fine. What **is discouraged** is accessing properties of `ElementRef.nativeElement.xxx` directly. – Günter Zöchbauer Jun 03 '16 at 12:33
  • I need to change the className of a child element (html element inside the template) of the component. I made this way: constructor(private myElement: ElementRef)... ngOnInit() { var el = this.myElement.nativeElement; el.children[0].className = 'new-class-name'; }. Is it correct? I was not able to retrieve the element using @ViewChild(). – Natanael Jun 12 '16 at 22:10
  • Günter, where is is said it's discouraged? – Natanael Jun 14 '16 at 10:27
  • 2
    @Natanael I don't know if or where this is explicitly documented but it is mentioned regularly in issues or other discussions (also from Angular team members) that direct DOM access should be avoided. Accessing the DOM directly (which is what accessing properties and methods of `ElementRef.nativeElement)` is, prevents you from using Angulars server side rendering and WebWorker feature (I don't know if it also breaks the upcoming offline template compiler - but I guess not). – Günter Zöchbauer Jun 14 '16 at 10:30
  • In some situations I need to define a value to an property like a class without use binding because binding can avoid other libraries to change the elements properties. The only way I found to do this is via direct property edition. – Natanael Jun 14 '16 at 10:36
  • You can do that. You just need to be aware of the disadvantages though. – Günter Zöchbauer Jun 14 '16 at 10:37
  • 11
    As mentioned above in the *read* section, if you want to get the nativeElement for an element with ViewChild, you have to do the following: `@ViewChild('myObj', { read: ElementRef }) myObj: ElementRef;` – jsgoupil Aug 18 '16 at 23:02
  • I have found that using a `ViewChild` setter works reliably: http://stackoverflow.com/a/39679241/882912 – KTCO Oct 02 '16 at 17:33
  • This is a good description of `ViewChild` - However the question is asking how to get the `

    ` element - In jQuery we could do `$('p')` or `$(input).next('p')`, how do we do this in Angular 2? How do we get the DOM element from the `ViewChild`?

    – Luke T O'Brien Dec 08 '16 at 10:17
  • You can add a template variable and use `@ViewChild('varName')` or inject `ElementRef` and use `this.elRef.nativeElement.querySelector('p')` – Günter Zöchbauer Dec 08 '16 at 10:47
  • @GünterZöchbauer, _if you want to subscribe to changes of the query result, it should be done in ngOnInit()_ - I assume it's [not yet implemented](https://github.com/angular/angular/issues/12818), correct? In `ngOnInit` the viewchildren property is not yet initialized – Max Koretskyi Feb 09 '17 at 08:51
  • I saw these comments, not exactly sure what they mean. – Günter Zöchbauer Feb 09 '17 at 14:30
  • How to get a hold of a **native element** from within a ContentChild? Tried with `@ContentChild(SomeComponent, { read: ElementRef })` but it did not work. It complained with *Can't construct a query for the property ... of ... since the query selector wasn't defined* – superjos May 02 '17 at 22:44
  • (I mean, beside trashing `@ContentChild` and turning to discouraged `ElementRef.nativeElement.querySelector()`) – superjos May 02 '17 at 22:55
  • What if you want to access to a dynamic class or id for example? THis seems to be good for static elements – Celso Soares Jan 11 '19 at 16:10
  • You can inject `ElementRef` to get a reference to the host element of the component and use `querySelector` or any other method DOM `Element` provides. There is no Angular way to access elements added dynamically through imperative DOM manipulation. – Günter Zöchbauer Jan 11 '19 at 20:56
  • new to Angular; so let me get this straight... this.elRef.nativeElement.querySelector('div'); is better than $('div'); Got It! And to think; we used to create web sites in HTML and [gulp] JavaScript... – iGanja Dec 12 '19 at 03:16
  • 1
    If you use `this.elRef.nativeElement.querySelector('div')` you are probably trying to use Angular as a jQuery alternative. Angular is wildly different. There are only rare cases where you need such code, for example if you integrating legazy (jQuery or similar components). In pure Angular bindings and services usually do the job. – Günter Zöchbauer Dec 12 '19 at 04:45
  • with `@ViewChild('myname') input; `, Do we need to use nativeElement property like`this.input.nativeElement.value`?? Isn't `this.input.value` just enough? – Tharindu Sathischandra Aug 21 '20 at 18:38
  • @TharinduSathischandra that's quite an old answer and I haven't used this in s long time. It should be easy enough to figure out. – Günter Zöchbauer Aug 21 '20 at 21:31
227

You can get a handle to the DOM element via ElementRef by injecting it into your component's constructor:

constructor(private myElement: ElementRef) { ... }

Docs: https://angular.io/docs/ts/latest/api/core/index/ElementRef-class.html

user3761308
  • 744
  • 3
  • 14
  • 30
Brocco
  • 62,737
  • 12
  • 70
  • 76
  • 1
    @Brocco can you update your answer? I'd like to see a current solution since `ElementRef` is gone. – Jefftopia Nov 24 '15 at 02:07
  • 26
    `ElementRef` is available (again?). – Günter Zöchbauer Feb 04 '16 at 19:15
  • 10
    [link](https://angular.io/docs/ts/latest/api/core/index/ElementRef-class.html) Use this API as the **last resort** when direct access to DOM is needed. Use templating and data-binding provided by Angular instead. Alternatively you take a look at Renderer which provides API that can safely be used even when direct access to native elements is not supported. Relying on direct DOM access creates tight coupling between your application and rendering layers which will make it impossible to separate the two and deploy your application into a web worker. – sandeep talabathula Apr 26 '17 at 10:40
  • @sandeeptalabathula What is a better option for finding an element to attach a floating date picker component from a third-party library to? I'm aware that this wasn't the original question, but you make it out that finding elements in the DOM is bad in all scenarios... – ProgrammingLlama Jul 24 '17 at 05:52
  • @john In my perspective, better option would be to create a custom angular2 ts component (an adapter to 3rd party lib) and use it as reusable throughout your app. I did for dialogs, flyouts etc,. – sandeep talabathula Jul 25 '17 at 06:55
  • @sandeeptalabathula That's what I've been doing, but it needed me to pass a DOM element to it. – ProgrammingLlama Jul 25 '17 at 07:29
  • 15
    @john Ah.. okay. You may try out this - `this.element.nativeElement.querySelector('#someElementId')` and pass ElementRef to the constructor like this.. `private element: ElementRef,` Import lib... `import { ElementRef } from '@angular/core';` – sandeep talabathula Jul 25 '17 at 08:05
  • `ElementRef` API should be the last resort when direct access to DOM is needed. `Renderer2` should be the preferred way. Using `ElementRef` might be a potential **security risk**!! https://angular.io/api/core/ElementRef#properties – GBra 4.669 Jan 25 '21 at 09:26
60
import { Component, ElementRef, OnInit } from '@angular/core';

@Component({
  selector:'display',
  template:`
   <input (input)="updateName($event.target.value)">
   <p> My name : {{ myName }}</p>
  `
})
class DisplayComponent implements OnInit {
  constructor(public element: ElementRef) {
    this.element.nativeElement // <- your direct element reference 
  }
  ngOnInit() {
    var el = this.element.nativeElement;
    console.log(el);
  }
  updateName(value) {
    // ...
  }
}

Example updated to work with the latest version

For more details on native element, here

gdi2290
  • 926
  • 5
  • 7
21

Angular 4+: Use renderer.selectRootElement with a CSS selector to access the element.

I've got a form that initially displays an email input. After the email is entered, the form will be expanded to allow them to continue adding information relating to their project. However, if they are not an existing client, the form will include an address section above the project information section.

As of now, the data entry portion has not been broken up into components, so the sections are managed with *ngIf directives. I need to set focus on the project notes field if they are an existing client, or the first name field if they are new.

I tried the solutions with no success. However, Update 3 in this answer gave me half of the eventual solution. The other half came from MatteoNY's response in this thread. The result is this:

import { NgZone, Renderer } from '@angular/core';

constructor(private ngZone: NgZone, private renderer: Renderer) {}

setFocus(selector: string): void {
    this.ngZone.runOutsideAngular(() => {
        setTimeout(() => {
            this.renderer.selectRootElement(selector).focus();
        }, 0);
    });
}

submitEmail(email: string): void {
    // Verify existence of customer
    ...
    if (this.newCustomer) {
        this.setFocus('#firstname');
    } else {
        this.setFocus('#description');
    }
}

Since the only thing I'm doing is setting the focus on an element, I don't need to concern myself with change detection, so I can actually run the call to renderer.selectRootElement outside of Angular. Because I need to give the new sections time to render, the element section is wrapped in a timeout to allow the rendering threads time to catch up before the element selection is attempted. Once all that is setup, I can simply call the element using basic CSS selectors.

I know this example dealt primarily with the focus event, but it's hard for me that this couldn't be used in other contexts.

UPDATE: Angular dropped support for Renderer in Angular 4 and removed it completely in Angular 9. This solution should not be impacted by the migration to Renderer2. Please refer to this link for additional information: Renderer migration to Renderer2

Neil T.
  • 3,308
  • 1
  • 25
  • 30
  • The class Renderer is DEPRECATED since Angular 4.3.0. https://angular.io/api/core/Renderer – Jamie Jul 17 '17 at 10:33
  • 1
    Can't we just use Renderer2 @Jamie ? [https://angular.io/api/core/Renderer2#selectRootElement](https://angular.io/api/core/Renderer2#selectRootElement) – Jordan Sep 03 '20 at 18:47
19

For people trying to grab the component instance inside a *ngIf or *ngSwitchCase, you can follow this trick.

Create an init directive.

import {
    Directive,
    EventEmitter,
    Output,
    OnInit,
    ElementRef
} from '@angular/core';

@Directive({
    selector: '[init]'
})
export class InitDirective implements OnInit {
    constructor(private ref: ElementRef) {}

    @Output() init: EventEmitter<ElementRef> = new EventEmitter<ElementRef>();

    ngOnInit() {
        this.init.emit(this.ref);
    }
}

Export your component with a name such as myComponent

@Component({
    selector: 'wm-my-component',
    templateUrl: 'my-component.component.html',
    styleUrls: ['my-component.component.css'],
    exportAs: 'myComponent'
})
export class MyComponent { ... }

Use this template to get the ElementRef AND MyComponent instance

<div [ngSwitch]="type">
    <wm-my-component
           #myComponent="myComponent"
           *ngSwitchCase="Type.MyType"
           (init)="init($event, myComponent)">
    </wm-my-component>
</div>

Use this code in TypeScript

init(myComponentRef: ElementRef, myComponent: MyComponent) {
}
jsgoupil
  • 3,788
  • 3
  • 38
  • 53
16

import the ViewChild decorator from @angular/core, like so:

HTML Code:

<form #f="ngForm"> 
  ... 
  ... 
</form>

TS Code:

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

class TemplateFormComponent {

  @ViewChild('f') myForm: ElementRef;
    .
    .
    .
}

now you can use 'myForm' object to access any element within it in the class.

Source

Hany
  • 1,310
  • 15
  • 17
  • But you should notice that you almost not need to access template elements in the component class, you just need to well understand the angular logic correctly. – Hany Nov 02 '17 at 14:47
  • 8
    Dont use any, the type is ElementRef –  Dec 19 '17 at 14:25
  • I ended up having to use 'any' because the element that I needed access to was another angular component which was wrapping a Kendo UI element, I needed to call a method on the component, which then calls a method on the Kendo element. – TomEberhard Dec 17 '20 at 21:45
13
 */
import {Component,ViewChild} from '@angular/core' /*Import View Child*/

@Component({
    selector:'display'
    template:`

     <input #myname (input) = "updateName(myname.value)"/>
     <p> My name : {{myName}}</p>

    `
})
export class DisplayComponent{
  @ViewChild('myname')inputTxt:ElementRef; /*create a view child*/

   myName: string;

    updateName: Function;
    constructor(){

        this.myName = "Aman";
        this.updateName = function(input: String){

            this.inputTxt.nativeElement.value=this.myName; 

            /*assign to it the value*/
        };
    }
}
Komal12
  • 3,340
  • 4
  • 16
  • 25
Mohamed Gabr
  • 430
  • 5
  • 18
  • 12
    Please provide some explanation to this code. Simply code dumping without explanation is highly discouraged. – rayryeng Jan 16 '17 at 14:38
  • 5
    This won't work: attributes set via @ViewChild annotations will only be available after ngAfterViewInit lifecycle event. Accessing the value in the constructor would yield an undefined value for `inputTxt` in that case. – David M. Mar 23 '17 at 19:38
9

I have used two way:

First way :

You can get a handle to the DOM element via ElementRef by injecting it into your component's constructor:

constructor(private myElement: ElementRef) {
this.myElement.nativeElement // <- your direct element reference
}

Second way:

@Component({
  selector: 'my-app',
  template:
  `
  <input #input value="enterThere">
  `,
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  @ViewChild('input') input:ElementRef; 

  ngAfterViewInit() {
    console.log(this.input);
  }
Sunderam Dubey
  • 1
  • 11
  • 20
  • 40
Naeem Bashir
  • 1,937
  • 20
  • 17
  • Is there a way to reference hidden elements? Because I'm trying that but doesn't work. I mean elements in the template that have `[hidden]=""`. – acarlstein Mar 09 '23 at 21:01
5

Note: This doesn't apply to Angular 6 and above as ElementRef became ElementRef<T> with T denoting the type of nativeElement.

I would like to add that if you are using ElementRef, as recommended by all answers, then you will immediately encounter the problem that ElementRef has an awful type declaration that looks like

export declare class ElementRef {
  nativeElement: any;
}

this is stupid in a browser environment where nativeElement is an HTMLElement.

To workaround this you can use the following technique

import {Inject, ElementRef as ErrorProneElementRef} from '@angular/core';

interface ElementRef {
  nativeElement: HTMLElement;
}

@Component({...}) export class MyComponent {
  constructor(@Inject(ErrorProneElementRef) readonly elementRef: ElementRef) { }
}
Aluan Haddad
  • 29,886
  • 8
  • 72
  • 84
  • 1
    This explains a problem I was having. This doesn't work because it'll say `item` needs to be an ElementRef, even though you're setting it to another ElementRef: `let item:ElementRef, item2:ElementRef; item = item2; // no can do.` . Very confusing. But this is fine: `let item:ElementRef, item2:ElementRef; item = item2.nativeElement` because of the implementation you pointed out. – oooyaya Mar 05 '17 at 03:24
  • 1
    Actually your first example `let item: ElementRef, item2: ElementRef; item = item2` fails because of definite assignment analysis. Your second fails for the same reasons but both succeed if `item2` is initialized for the reasons discussed (or as a useful quick check for assignability we can use `declare let` here). Regardless, truly a shame to see `any` on a public API like this. – Aluan Haddad Mar 05 '17 at 23:29
3

Mimimum example for quick usage:

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

@Component({
  selector: 'my-app',
  template:
  `
  <input #inputEl value="hithere">
  `,
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  @ViewChild('inputEl') inputEl:ElementRef; 

  ngAfterViewInit() {
    console.log(this.inputEl);
  }
}
  1. Put a template reference variable on the DOM element of interest. In our example this is the #inputEl on the <input> tag.
  2. In our component class inject the DOM element via the @ViewChild decorator
  3. Access the element in the ngAfterViewInit lifecycle hook.

Note:

If you want to manipulate the DOM elements use the Renderer2 API instead of accessing the elements directly. Permitting direct access to the DOM can make your application more vulnerable to XSS attacks

Willem van der Veen
  • 33,665
  • 16
  • 190
  • 155
2

to get the immediate next sibling ,use this

event.source._elementRef.nativeElement.nextElementSibling
Apoorv
  • 1,338
  • 1
  • 17
  • 18
2

For components inside *ngIf, another approach:

The component I wanted to select was inside a div's *ngIf statement, and @jsgoupil's answer above probably works (Thanks @jsgoupil!), but I ended up finding a way to avoid using *ngIf, by using CSS to hide the element.

When the condition in the [className] is true, the div gets displayed, and naming the component using # works and it can be selected from within the typescript code. When the condition is false, it's not displayed, and I don't need to select it anyway.

Component:

@Component({
    selector: 'bla',
    templateUrl: 'bla.component.html',
    styleUrls: ['bla.component.scss']
})
export class BlaComponent implements OnInit, OnDestroy {
    @ViewChild('myComponentWidget', {static: true}) public myComponentWidget: any;
    @Input('action') action: ActionType; // an enum defined in our code. (action could also be declared locally)

constructor() {
   etc;
}

// this lets you use an enum in the HMTL (ActionType.SomeType)
public get actionTypeEnum(): typeOf ActionType {
    return ActionType;
}

public someMethodXYZ: void {
    this.myComponentWidget.someMethod(); // use it like that, assuming the method exists
}

and then in the bla.component.html file:

<div [className]="action === actionTypeEnum.SomeType ? 'show-it' : 'do-not-show'">

    <my-component #myComponentWidget etc></my-component>
</div>
<div>
    <button type="reset" class="bunch-of-classes" (click)="someMethodXYZ()">
        <span>XYZ</span>
    </button>
</div>   

and the CSS file:

 ::ng-deep {
    .show-it {
         display: block;   // example, actually a lot more css in our code
    }
    .do-not-show {
        display: none'; 
    }
}
TomEberhard
  • 907
  • 10
  • 11
1

Selecting target element from the list. It is easy to select particular element from the list of same elements.

component code:

export class AppComponent {
  title = 'app';

  listEvents = [
    {'name':'item1', 'class': ''}, {'name':'item2', 'class': ''},
    {'name':'item3', 'class': ''}, {'name':'item4', 'class': ''}
  ];

  selectElement(item: string, value: number) {
    console.log("item="+item+" value="+value);
    if(this.listEvents[value].class == "") {
      this.listEvents[value].class='selected';
    } else {
      this.listEvents[value].class= '';
    }
  }
}

html code:

<ul *ngFor="let event of listEvents; let i = index">
   <li  (click)="selectElement(event.name, i)" [class]="event.class">
  {{ event.name }}
</li>

css code:

.selected {
  color: red;
  background:blue;
}
Stphane
  • 3,368
  • 5
  • 32
  • 47
Sai Goud
  • 49
  • 4
0

In case you are using Angular Material, you can take advantage of cdkFocusInitial directive.

Example: <input matInput cdkFocusInitial>

Read more here: https://material.angular.io/cdk/a11y/overview#regions

Djidel
  • 347
  • 4
  • 9
0

From the template, directly pass template variable to method -

Taking a simple checkbox toggle state example - on clicking the button it will check or uncheck the checkbox.

toggleCheckBoxStatus(inputCheckbox) {
  inputCheckbox.checked = !inputCheckbox.checked;
}
<input type="checkbox" #inputCheckbox>
<button (click)="toggleCheckBoxStatus(inputCheckbox)">Toggle Check Box</button>
Subhadeep
  • 149
  • 5