Angular icon Get 67% off the Angular Master Bundle!

See the bundle then add to cart and your discount is applied.

days
hours
mins
secs

Getting Element references in Angular templates

May 26, 2020 4 mins read

Angular post
Angular icon

Want expert Angular skills? Here's what you need to know.

Show Me View Angular courses

In this post you’re going to learn how to reference an element inside an Angular template.

This is typically known as a “reference” variable, in React they’re called “refs” too, so the concept is much the same.

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?

Exploring JavaScript Array Methods cover

⚡️ FREE eBook: 🔥 ForEach, Map, Filter, Reduce, Some, Every, Find

Todd Motto “This book is straight to the point, syntax exploration, comprehensive guide, real-world examples, tips and tricks - it covers all you need Todd Motto, author of Exploring JavaScript Array Methods

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.

⚠ Warning! 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:

Exploring JavaScript Array Methods cover

⚡️ FREE eBook: 🔥 ForEach, Map, Filter, Reduce, Some, Every, Find

Todd Motto “This book is straight to the point, syntax exploration, comprehensive guide, real-world examples, tips and tricks - it covers all you need Todd Motto, author of Exploring JavaScript Array Methods

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!

Exploring JavaScript Array Methods cover

⚡️ FREE eBook: 🔥 ForEach, Map, Filter, Reduce, Some, Every, Find

Todd Motto “This book is straight to the point, syntax exploration, comprehensive guide, real-world examples, tips and tricks - it covers all you need Todd Motto, author of Exploring JavaScript Array Methods

You'll also like...