Hassan
Hassan

Reputation: 2408

How to focus on a field?

I updated from Angular 2 to Angular 4 and in the docs it's written to use the Renderer2 instead of Renderer which is deprecated.

Now am looking into the Renderer source, but cannot find a way to invoke the focus() method as I used to.

Old method:

this.renderer.invokeElementMethod(element, 'focus', []);

What is the new aproach?

EDIT

What if the element i am focusing onto is obtained via QuerySelector?

For instance:

let inputField = document.querySelectorAll('.dialog input');
if ( inputField[0] ) {
   inputField[0].focus();
}

Since its obtained via QuerySelector, the focus() method doesn't exist on it.

Upvotes: 13

Views: 34695

Answers (6)

Chen.so
Chen.so

Reputation: 303

If you are using Angular CDK, you can set focus using FocusMonitor.focusVia method that is part of @angular/cdk/a11y Accessibility module (A11yModule).

This way you can avoid any DOM manipulation and avoid referencing nativeElement altogether.


import { FocusMonitor } from '@angular/cdk/a11y';


export class ExampleComponent implements AfterViewInit {
  @ViewChild('elem', {static: false}) elemRef;

  constructor(private focusMonitor: FocusMonitor) {
  }

  ngAfterViewInit() {
    // Programmatically set focus. Focus source is 'program'.
    this.focusMonitor.focusVia(this.elemRef, 'program');
  }
}

Upvotes: 4

Pedro Ferreira
Pedro Ferreira

Reputation: 493

If you declare the correct type of your inputField var, you can use any JS query selector.

For instances declaring:
let inputField: HTMLInputElement = document.querySelector('.dialog input');
enables you to call
inputField.focus();
that it will work and no TS error will be thrown.

Upvotes: 2

Ben Atlas
Ben Atlas

Reputation: 127

At this time, it appears that this task is not achievable without directly modifying the DOM. As others have said, the invokeElementMethod() is deprecated. However, you will find issues with using selectRootElement as well, as this is not its intended purpose and you will end up losing the children inside of your DIV. Here is a link to a SO question that explains selectRootElement in more detail (since the angular docs are atrocious):

Renderer multiple selectRootElement Issue (with plnkr provided)

As for now, it appears the only way to easily achieve your goal is to use a template reference variable, a view child, and the yourViewChild.nativeElement.focus() (though this practice is also not recommended)

There is also a suggested workaround using a FocusService found on this GitHub issue: https://github.com/angular/angular/issues/15674

In this issue, Tytskyi mentions that you can implement a FocusService and provide different implementations of focus() based on AppModuleBrowser and AppModuleServer. I'm not sure of the fine details of how that would work but it may be the only way to achieve this without using nativeElement at this time.

Upvotes: 3

seytzhan
seytzhan

Reputation: 527

Also you can use selectRootElement method of Renderer2.

For example:

constructor(private renderer: Renderer2) {}

this.renderer.selectRootElement('#domElementId').focus()

, where domElementId is id='domElementId' in your html

Upvotes: 15

Poul Kruijt
Poul Kruijt

Reputation: 71891

The invokeElementMethod is deprecated, and will not find its way back into the new renderer. To set focus to an element, you can simply use this now:

element.nativeElement.focus();

If you are using document.querySelectorAll, you are doing something not the angular way, and you should find another way to do it. If there is no other way to do it, then the same principle applies. The focus() method is plain javascript, so you can use it within angular2/typescript. Be sure to do the querySelectorAll inside the ngAfterViewInit hook of the component though:

ngAfterViewInit() {
   let inputField: HTMLElement = <HTMLElement>document.querySelectorAll('.dialog input')[0];
   inputField && inputField.focus();
}

Upvotes: 20

Vikas Mahajan
Vikas Mahajan

Reputation: 355

template reference variable :#inlineEditControl
<input #inlineEditControl class="form-control form-control-lg"  [placeholder]="label">

 @ViewChild('inlineEditControl') inlineEditControl: ElementRef; // input DOM element

this.inlineEditControl.nativeElement.focus();

Upvotes: 6

Related Questions