16

Similar to Google Login page, I want to have autofocus on input element after on click event. I have tried @ViewChild('id') and document.getElementId('id'). Both of it doesn't work. It's always null or undefined. How can I achieve this?

       <mat-form-field class="example-full-width col-sm-4">
        <input autofocus id="login-username" matInput 
       formControlName="userName" minlength="5" maxlength="20" 
       name="userName" placeholder="Username" required #input> 
      </mat-form-field>

        <div class="col-sm-12">
           <button (click)="changeView()" type="button" mat-raised-
             button color="primary">Next</button>
          </div>

         <mat-form-field class="example-full-width col-sm-4" 
          #passwordInput>
        <input autofocus type="password" matInput 
       placeholder="Password" minlength="5" maxlength="20" 
       formControlName="userPassword" #passwordInput>  
      </mat-form-field>
4
  • Pl show some code from where in HTML you are referencing @ViewChild Commented Jul 25, 2018 at 4:45
  • I am declaring @ViewChild('passwordInput') before constructor and using it in the changeView() when called. like this. changeView(){ this.passwordInputRef.nativeElement.focus(); } But this throws error. Says nativeElement is undefined. Commented Jul 25, 2018 at 5:05
  • ViewChild properties aren't set until the AfterViewInit lifecycle hook Commented Jul 25, 2018 at 5:10
  • 1
    you need to add a timeout before setting focus(), or it will throw nativeElement undefined error Commented Jul 25, 2018 at 5:30

6 Answers 6

28

Unfortunately not every solution consistently works with autofocusing matInput at the moment of rendering. Here is what I tried:

  • HTML autofocus attribute
  • JS input.focus()
  • cdkFocusInitial from @angular/cdk

All of the above methods might work but under some conditions they appear to be broken.

What consistently and always works is using a high-level api of the matInput. Here is a simple directive that uses this approach:

import { Directive, OnInit } from '@angular/core';
import { MatInput } from '@angular/material/input';

@Directive({
  selector: '[matInputAutofocus]',
})
export class AutofocusDirective implements OnInit {

  constructor(private matInput: MatInput) { }

  ngOnInit() {
    setTimeout(() => this.matInput.focus());
  }

}

The timeout is required to delay focusing the element because matInput does not properly function at the moment of creating yet. Usage:

<mat-form-field>
  <input type="text" matInput matInputAutofocus>
</mat-form-field>

Of course, naming the selector matInputAutofocus is dangerous because material itself can come to this solution one day. Use it on your own risk or just rename with your prefix (recommended).

Focus and meanwhile select the input value

A cherry on the cake is adding the possibility to also preselect the content of the input (this is most of the time more user-friendly), which slightly changes the implementation:

import { Directive, OnInit } from '@angular/core';
import { MatInput } from '@angular/material/input';
    
@Directive({
  selector: '[matInputAutofocus]',
})
export class AutofocusDirective implements OnInit {

  @Input()
  autofocusSelectValue = false;

  constructor(
    private matInput: MatInput,
    private elRef: ElementRef<HTMLInputElement>,
  ) { }

  ngOnInit(): void {
    setTimeout(() => {
      this.matInput.focus();

      if (this.autofocusSelectValue) {
        this.elRef.nativeElement.setSelectionRange(0, this.elRef.nativeElement.value.length);
      }
    });
  }

}

Use it as:

<mat-form-field>
  <input type="text" matInput matInputAutofocus [autofocusSelectValue]="true">
</mat-form-field>
Sign up to request clarification or add additional context in comments.

5 Comments

Another advantage of this solution over using accessing ElementRef using <elementRef variable>.nativeElement.focus() in ngAfterViewInit() is that you won't need to write ngAfterViewInit() for every component that uses matInput in it's template.
MatInput import should be: import { MatInput } from '@angular/material/input';
@smnbbrv When I use this on an array of mat-form-fields created by a *ngFor-Loop, the focus is always set on the last element of the array. Do you have any idea how to fix that ?
@Phreneticus I guess the easiest is to add @Input('matInputAutofocus') enabled: boolean;, run the ngOnInit part only in case it is true, and in ngFor set the property to true if it is first (or another desired location), something like <div *ngFor="let a of array; let first = first">...[matInputAutofocus]="first"
Because this answer still works well, I'll add another suggested typo fix. Instead of input.value.length, you want this.matInput.value.length.
10

When you use Angular Material, you can use cdkFocusInitial to tell the library where you want to focus after component is ready. No native approaches are required:

<mat-form-field class="example-full-width">
  <mat-label>My Label</mat-label>
  <textarea 
    cdkFocusInitial           <---------
    cdkTextareaAutosize
    matInput>
  </textarea>
</mat-form-field>

1 Comment

what module should be imported to make this work?
9

If you want to set focus to an Input field as soon as the page loads, you can do it simply by applying HTML attribute to that element, such as :

<input type="text" #input1 autofocus>

Else if you want to do this from your component.ts conditionally, use:

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

 @ViewChild('input1') inputEl:ElementRef;

 ngAfterViewInit() {
 this.inputEl.nativeElement.focus();
 }

Or you can use:

First import the renderer2 from @angular/core and then,

    const element = this.renderer.selectRootElement('#input1');

 setTimeout(() => element.focus(), 0);

Whatever you seems to be comfortable with your existing code.

1 Comment

Simples e Funcional!
4

You can use cdkFocusInitial from Material.

<mat-form-field>
    <input matInput placeholder="username" cdkFocusInitial>
</mat-form-field>

See orginal answer.

Comments

0

You need to use template ref to input element then use ViewChild it will work

Example: https://stackblitz.com/edit/angular-frqm8b

 <mat-form-field class="example-full-width col-sm-4">
        <input autofocus id="login-username" matInput 
       formControlName="userName" minlength="5" maxlength="20" 
       name="userName" placeholder="Username" required #input> 
      </mat-form-field>

        <div class="col-sm-12">
           <button (click)="changeView()" type="button" mat-raised-
             button color="primary">Next</button>
          </div>

         <mat-form-field class="example-full-width col-sm-4" 
          >
        <input autofocus type="password" matInput 
        ****#passwordInput****
       placeholder="Password" minlength="5" maxlength="20" 
       formControlName="userPassword" #passwordInput>  
      </mat-form-field>

Comments

0

I did a mixture of all the other answers. This was the only way it worked for me.

Add to any Typescript function

  @ViewChild('foobarElement') foobarElement: ElementRef;
      
  anyFunction() {
      setTimeout(() => this.foobarElement.nativeElement.focus());
  }

And in the HTML

  <input type="text"
    matInput 
    #foobarElement>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.