Angular Focus Done Right: Material Focus Form Input with Renderer2

Angular is a very popular dynamic application framework for web, desktop and mobile platforms. A frequent question that pops up in stack overflow is How to focus on form input field using Angular Material? or How to focus on form input field programmatically from typescript code in Angular?

In this post, I will show you how to use Renderer2 to focus on a form input. We will be using Renderer2 to focus a form input in Angular Macterial, which is compatible with reactive forms approach.

Talk is cheap. Show me the code

Linus Torvalds

Here’s a live demo on how to focus on form input in Angular Material:

Popular but bad solution: Use ElementRef

I have seen many Angular examples that involve using ElementRef to get access to the native HTML element and calling focus(). The following is from a recent example I have seen from codeburst.

Focus on HTML input with ElementRef in Angular

In this approach, you reference the HTML input element with ViewChild in typescript code. Once Angular initialises the view, the reference will contain a native element that can be used for manipulation.

What’s wrong with ElementRef?

ElementRef is included in Angular core library. It is a wrapper around a native HTML element inside of a View.

Security Risks

ElementRef in Angular gives direct access to HTML DOM (Document Object Model). Allowing direct access to the DOM can make your application vulnerable to XSS attacks.

You should always avoid using ElementRef in Angular.

Separation of concerns

Relying on ElementRef for HTML DOM operations like form input focus and blur creates tight coupling between the application and rendering layers. So if you wanted use web workers, it will be impossible to separate the application logic from DOM manipulation code.

Focus with Renderer2

Renderer2 is a service that comes with the core Angular package. It allows DOM manipulation in a safe way.

It’s a bit like jQuery for Angular.

I have setup a reactive form below but this is optional. All you need is a unique id value on the input element.

<form [formGroup]="form">
	<mat-form-field class="full-width">
		<input matInput id="myInput" formControlName="myInput" placeholder="Focused Input">
  </mat-form-field>
<br>
  <button mat-raised-button color="primary" (click)="focusMyInput()">Focus</button>
</form>

Inject Renderer 2 in the constructor and access the input element with selectRootElement() method. Once you get the element, you can focus on it.

constructor(private renderer: Renderer2) {}

focusMyInput() {
  this.renderer.selectRootElement('#myInput').focus();
}

Additionally, you can focus on input element on load, using the ngAfterViewInit() hook.

ngAfterViewInit() {
  setTimeout(() => {

      var elem = this.renderer.selectRootElement('#myInput');

      this.renderer.listen(elem, "focus", () => { console.log('focus') });

      this.renderer.listen(elem, "blur", () => { console.log('blur') });
  
      elem.focus();

  }, 1000);
}

Another useful feature of Renderer2 is that you can listen for events on elements. For example, you can subscribe to the focus and blur events of the input with listen() method. In the following code snippet, we are grabbing the input element with Renderer2 and subscribing to focus and blur events.

It is important that you event subscription in ngAfterViewInit() hook as the element won’t be initialised in ngOnInit().

Summary

To summarise, we’ve seen how to use Renderer2 to focus form input in Angular Material. Other approaches use ElementRef to access native HTML element but this is not recommended due to security risks.

Umut Esen

Umut is an enthusiastic software developer, latest web and mobile technology adapter and primary author of onthecode.

Leave a Reply

Close Menu