34

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 .

Zhu
  • 3,679
  • 14
  • 45
  • 76

11 Answers11

36

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

<mat-form-field>
    <input matInput placeholder="username" #usernameInput cdkFocusInitial>
</mat-form-field>
Krishna
  • 6,107
  • 2
  • 40
  • 43
kumardippu
  • 599
  • 4
  • 11
  • 7
    This does not work. If so please provide a stackblitz example – Stefan Feb 05 '19 at 09:11
  • Make sure cdkFocusInitial used only once in a form. Trust me it works. – kumardippu Feb 06 '19 at 10:17
  • 18
    @kumardippu `Trust me it works` - I wouldn't trust myself without seeing a working example ;-) – Mubin Mar 09 '19 at 15:13
  • 2
    Placing cdkFocusInitial on a `mat-slide-toggle` worked for me – speckledcarp Sep 24 '19 at 16:42
  • 1
    Worked for me, when I opened a `MatDialog`, the focus was on the `MatExpansionPanel` but I wanted it on the button. Setting `cdkFocusInitial` on the button did exactly that. – dylanvdb Apr 24 '20 at 13:32
  • 6
    It's not a matter of trust. It's a matter of troubleshooting. A working example could reveal what the op is missing. Could be something as simple as a package version, or a syntax difference, or something else interfering. – Stoutie May 05 '20 at 21:36
28

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 ...

jspassov
  • 791
  • 7
  • 11
16

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') firstname: ElementRef;

    ngOnInit() {
      this.firstname.nativeElement.focus();
    }
    
}
jenson-button-event
  • 18,101
  • 11
  • 89
  • 155
marioosh
  • 27,328
  • 49
  • 143
  • 192
  • 6
    This was the right answer for me (Angular 9, Material 9). For typing die-hards, use `ElementRef` instead of `any`; (And `static: true` isn't needed) – Russ May 04 '20 at 20:12
15

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>
Prashant
  • 4,775
  • 3
  • 28
  • 47
  • 7
    yes,for the first time it works if I navigate between two forms using button click's without any interaction with input fields it not working from the second time it's not working @Prashant – Zhu Sep 03 '18 at 04:41
  • 1
    This is working well in a form with Angular 10 + material, thanks ! – Elvynia Oct 19 '20 at 18:18
15

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();
    }
}
Aniket Avhad
  • 4,025
  • 2
  • 23
  • 29
  • 2
    this.nameInput is `undefined` when you use it inside `ngOnInit`, you need to call it inside `ngAfterViewInit` but then you get the error metioned by [@jspassov](https://stackoverflow.com/a/54794081/12163165) – user12163165 Jul 18 '20 at 11:26
  • I do not believe that almost two years have passed and no solution has yet been found to the problem – user12163165 Jul 18 '20 at 11:34
6

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.

Stavm
  • 7,833
  • 5
  • 44
  • 68
3

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();
  });
}
que1326
  • 2,227
  • 4
  • 41
  • 58
3

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.

KamilK
  • 31
  • 2
1

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

<mat-form-field>
    <input matInput placeholder="username" cdkFocusInitial>
</mat-form-field>
Abraham
  • 560
  • 1
  • 4
  • 12
0

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.

Bob Ramsey
  • 547
  • 1
  • 6
  • 13
0

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')
Darren Street
  • 1,652
  • 17
  • 21