Get super confident at Angular. Angular Icon

I promise to change your Angular skills!

Getting Element references (ElementRef) in Angular templates

In this post you’re going to learn how to select an element in an Angular template.

The concept of getting a reference to an element is typically known as a “reference variable” (even in React they’re called “refs”).

Element references inside the template

First, let’s create an <input> and give it an ngModel directive:

<input ngModel>

Notice here how I’m not using the typical [(ngModel)] syntax, as here we just want to allow Angular to set a local model and update on change. This allows us to then log out the model value in a different way momentarily!

To get a direct reference to the DOM element, we’ll introduce Angular’s ref syntax - the hash # symbol. Adding it to our <input> would allow us to do something interesting:

<input ngModel #username>
<p>{{ username.value }}</p>

We’ve now used #username (you can call these template ref variables anything you like) and because it’s a reference to our <input ngModel>, it’s actually re-binding the value under-the-hood each time we type.

As #username is now a reference variable bound to the Angular component, we can access it anywhere - which is exactly what I’m doing with {{ username.value }} and logging out the value!

Start typing in the live Stackblitz demo to see the model reflect as we type, note we’re using the username reference exports the reference to the DOM Node:

Element references inside a Component class

So we know how to access our #username inside the template, but what about the component?

For this, we need to introduce ViewChild, which can be used as a decorator on a property:

@Component({...})
export class AppComponent {
  @ViewChild('username') input;
}

Now anywhere inside the class we can reference this.input and get the element ref! But, what type is this?

Angular Directives In-Depth eBook Cover

Free eBook

Directives, simple right? Wrong! On the outside they look simple, but even skilled Angular devs haven’t grasped every concept in this eBook.

  • Green Tick Icon Observables and Async Pipe
  • Green Tick Icon Identity Checking and Performance
  • Green Tick Icon Web Components <ng-template> syntax
  • Green Tick Icon <ng-container> and Observable Composition
  • Green Tick Icon Advanced Rendering Patterns
  • Green Tick Icon Setters and Getters for Styles and Class Bindings

A ViewChild can return a few different things, one being an ElementRef - an element reference! This looks like:

class ElementRef<T> {
  constructor(nativeElement: T)
  nativeElement: T
}

This <T> is a generic in TypeScript, which means we should supply further type information for it to infer down to the constructor and also the nativeElement property. Here’s how we can do that:

@Component({...})
export class AppComponent {
  @ViewChild('username') input: ElementRef<HTMLInputElement>;
}

So surely now we can just go ahead and access this.input?

@Component({...})
export class AppComponent {
  @ViewChild('username') input: ElementRef<HTMLInputElement>;

  constructor() {
    // undefined
    console.log(this.input);
  }
}

Inside the constructor our this.input reports as undefined, and moving this console.log to ngOnInit solves the issue as the component template is ready then:

So surely now we can just go ahead and access this.input?

@Component({...})
export class AppComponent implements OnInit {
  @ViewChild('username') input: ElementRef<HTMLInputElement>;

  ngOnInit() {
    // ElementRef { nativeElement: <input> }
    console.log(this.input);
  }
}

We can totally use ngOnInit, but there’s actually a lifecycle hook built exclusively for when our ViewChild will be ready - and that’s the AfterViewInit hook. Let’s refactor:

@Component({...})
export class AppComponent implements AfterViewInit {
  @ViewChild('username') input: ElementRef<HTMLInputElement>;

  ngAfterViewInit() {
    // ElementRef { nativeElement: <input> }
    console.log(this.input);
  }
}

So to use the native <input> we’ll need to reference this.input.nativeElement each time.

👏 It’s advised not to use properties on this.input.nativeElement directly however - use the Renderer2 class!

The docs for ElementRef suggest “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.”

I agree, so let’s do it properly.

The Renderer2 is considered the preferred option. Putting everything together, our final solution ends up as follows:

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

@Component({
  selector: 'my-app',
  template: `
  <div>
    <input ngModel #username>
    <p>{{ username.value }}</p>
  </div>
  `
})
export class AppComponent implements AfterViewInit {
  @ViewChild('username') input: ElementRef<HTMLInputElement>;

  constructor(private renderer: Renderer2) {}
  
  ngAfterViewInit() {
    // ElementRef: { nativeElement: <input> }
    console.log(this.input);

    // Access the input object or DOM node
    console.dir(this.input.nativeElement);

    // Manipulate via Renderer2
    this.renderer.setStyle(this.input.nativeElement, 'background', '#d515a0');
  }
}

I’ve included a Stackblitz embed again to demonstrate the full setup with Renderer2 and ElementRef:

Summary

Lots of things to think about here when we want to dive into the DOM alongside Angular, just ensure if you need to touch the native DOM elements that you use the right approach via the Renderer2 class.

The most powerful methods come from referencing the DOM element inside the class, but we started out learning about template reference variables and moved onto using class methods and the AfterViewInit lifecycle hook.

We’ve covered how to use ViewChild and how it can return us an ElementRef, and we’ve explored the .nativeElement property on the @ViewChild('username') input property and learned how to use a generic type.

If you are serious about your Angular skills, your next step is to take a look at my Angular courses where you’ll learn Angular, TypeScript, RxJS and state management principles from beginning to expert level.

Happy coding!

Related blogs 🚀

Free eBooks:

Angular Directives In-Depth eBook Cover

JavaScript Array Methods eBook Cover

NestJS Build a RESTful CRUD API eBook Cover