26
votes

I am new to angular ,In my application I have a mat-dialog in which I have two forms namely Login and SignUp.

Once I open the dialog first time the auto focus is set on username field.problem is when I navigate to SignUp form on button click that form's FIrst Name field not get auto focused ,the same way navigate from signup to login the username field is now not get auto focused.

I have tried to some stackoverflow solutions but nothing is resolved my issue.

popupScreen.component.html

<form class="login" *ngIf="isLoginHide">
    <mat-form-field>
        <input matInput placeholder="username">
    </mat-form-field>
    <mat-form-field>
        <input matInput placeholder="password">
    </mat-form-field>

    <button mat-button color="primary">Login</button>
    <button mat-button (click)="hideLogin($event)" color="accent">SignUp</button>
</form>

<form class="SignUp" *ngIf="isSignUpHide">
    <mat-form-field>
        <input matInput placeholder="First Name">
    </mat-form-field>
    <mat-form-field>
        <input matInput placeholder="Last Name">
    </mat-form-field>
    <mat-form-field>
        <input matInput placeholder="username">
    </mat-form-field>
    <mat-form-field>
        <input matInput placeholder="password">
    </mat-form-field>

    <button mat-button (click)="hideSignUp($event)" color="primary">Login</button>
    <button mat-button color="accent">SignUp</button>
</form>

can anyone help me to solve this .

11

11 Answers

29
votes

Kindly use cdkFocusInitial attribute in input field where you want to focus.

<mat-form-field>
    <input matInput placeholder="username" #usernameInput cdkFocusInitial>
</mat-form-field>
25
votes

Almost .. :-), If you are using some Materials component like MatSelect - above solutions does not work. Below you can find a solution that work for me.

<mat-form-field>
    <mat-select #myElement ......>
       ..........
    </mat-select>
<mat-form-field>

then in component...

@ViewChild('myElement') firstItem: MatSelect;

ngAfterViewInit() {
   this.firstItem.focus();
   this.cd.detectChanges();
}

bear in mind that you have to force change detection after setting focus to avoid Angular error : "ExpressionChangedAfterItHasBeenCheckedError" (btw you have to include also "ChangeDetectorRef" in your component constructor)

Hope this help ...

15
votes

This works for me in Angular 8:

<mat-form-field>
    <input matInput placeholder="First Name" #firstname>
</mat-form-field>

.ts

export class TestComponent implements OnInit {

    @ViewChild('firstname', {static: true}) firstname:any;

    ngOnInit() {
      this.firstname.nativeElement.focus();
    }

}
14
votes

Have you tried adding autofocus to the form field for First name?

<form class="SignUp" *ngIf="isSignUpHide">
    <mat-form-field>
        <input matInput placeholder="First Name" autofocus>
    </mat-form-field>
    <mat-form-field>
        <input matInput placeholder="Last Name">
    </mat-form-field>
    <mat-form-field>
        <input matInput placeholder="username">
    </mat-form-field>
    <mat-form-field>
        <input matInput placeholder="password">
    </mat-form-field>

    <button mat-button (click)="hideSignUp($event)" color="primary">Login</button>
    <button mat-button color="accent">SignUp</button>
</form>
14
votes

You can use MatInput for set autofocus

here's is an example,

in component.html

<mat-form-field>
    <input matInput placeholder="First Name" #firstname="matInput">
</mat-form-field>

and in component.ts

import { MatInput } from '@angular/material/input';

export class Component implements OnInit {

    @ViewChild('firstname') nameInput: MatInput;

    ngOnInit() {
       this.nameInput.focus();
    }
}
5
votes

use can use native element's focus.

havn't tested this code, but something like this:

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

on your component:

@ViewChild('usernameInput') usrFld: ElementRef;

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

p.s: you should be routing to a signup component rather than using ngIf for navigation.

3
votes

This works for me:

<mat-label >Radio name:</mat-label>
   <input type="text" #inputName="matInput" matInput formControlName="name" placeholder="Type radio name"/>
</mat-form-field>

  @ViewChild('inputName', { static: false }) inputName: MatInput;

  ngAfterViewInit(): void {
    setTimeout(() => this.inputName.focus(), 0);
  }

setTimeout is rather a hack to avoid change detection issue. I don't know why, but executing change detecion manually doesn't work in my case.

1
votes

For MatDialog you will have to delay the action with setTimeout

<mat-form-field>
  <mat-label>Name</mat-label>
  <input #inputName="matInput" matInput formControlName="name">
</mat-form-field>

@ViewChild('inputName') inputName: MatInput;

ngAfterViewInit() {
  setTimeout(() => {
    this.inputName.focus();
  });
}
0
votes

Be aware that depending on when and how your component is displayed, ngOnInit may not be the place to set the focus.

For example, if ComponentOne's html looks like this:

  <ng-container *ngIf='something === true'>
    <app-component-two></app-component-two>
  </ng-container>

And ComponentTwo has your input element and ComponentTwo's ts file is set up correctly, then putting this in your component-two.component.ts file:

ngOnInit() {
  this.myInput.focus();
}

May not work because the html for ComponentTwo has not been rendered yet, meaning that even with the ViewChild correct, this.myInput has not been rendered and is null.

ngAfterViewInit() {
  this.myInput.focus();
}

Even if you are not nesting components, you may still need to pay attention to the lifecycle of the html to make sure that your focus command occurs after the input element has been rendered.

Check out https://angular.io/guide/lifecycle-hooks#lifecycle-event-sequence for more information about the angular lifecycle.

0
votes

Such a simple and ordinary thing to do but most of above didn't work for me.

The cdkFocusInitial works great unless you are using a stepper control or equivalent where you want a new input to be focussed on stepper.next().

I ended up NOT using viewchild to get a reference to the object, and instead good old fashioned ids.

public setFocus(input: string) {
   const targetElem = document.getElementById(input);
   setTimeout(function waitTargetElem() {
    if (document.body.contains(targetElem)) {
      targetElem.focus();
    } else {
      setTimeout(waitTargetElem, 100);
    }
  }, 500);
}

call it with...

this.setfocus('id of your input')
0
votes

The solution for this is very simple, Use Angular Material CDK option cdkFocusInitial

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