Focus input after going to the next step in MatStepper

与世无争的帅哥 提交于 2021-02-07 12:35:28

问题


How can I programmatically set focus to "Address" input after clicking "Next" button on step one, so after going form step 1 ("Fill out your name") to step 2 ("Fill out your address").

https://stackblitz.com/angular/onnbkqrydrg?file=app%2Fstepper-overview-example.ts https://material.angular.io/components/stepper/overview

This is what I want to achieve:

I was already experimenting with MatStepper's selectionChange event, but it seems that it is fired before step 2 is visible, so it doesn't work.


回答1:


You were on the right path using the selection Change event. See the forked StackBlitz:

StackBlitz

What you want to do is assign an id to your input elements that can be used in correlation with the selected index of the stepper, such as:

<mat-step [stepControl]="firstFormGroup">
    <form [formGroup]="firstFormGroup">
      <ng-template matStepLabel>Fill out your name</ng-template>
      <mat-form-field>
        <input id="input0" matInput placeholder="Last name, First name" formControlName="firstCtrl" required>
      </mat-form-field>
      <div>
        <button mat-button matStepperNext>Next</button>
      </div>
    </form>
  </mat-step>
  <mat-step [stepControl]="secondFormGroup">
    <form [formGroup]="secondFormGroup">
      <ng-template matStepLabel>Fill out your address</ng-template>
      <mat-form-field>
        <input  id="input1" matInput placeholder="Address" formControlName="secondCtrl" required>
      </mat-form-field>
      <div>
        <button mat-button matStepperPrevious>Back</button>
        <button mat-button matStepperNext>Next</button>
      </div>
    </form>
  </mat-step>

Then by using the selection (selectionChange) event you can set focus on the input element after waiting for it to be displayed in the DOM:

export class StepperOverviewExample implements AfterViewInit {
  isLinear = false;
  firstFormGroup: FormGroup;
  secondFormGroup: FormGroup;

  public targetInput = 'input0';

  constructor(private _formBuilder: FormBuilder) { }

  ngOnInit() {
    this.firstFormGroup = this._formBuilder.group({
      firstCtrl: ['', Validators.required]
    });
    this.secondFormGroup = this._formBuilder.group({
      secondCtrl: ['', Validators.required]
    });
  }

  ngAfterViewInit() {
    this.setFocus();
  }

  private setFocus() {
    let targetElem = document.getElementById(this.targetInput);
    setTimeout(function waitTargetElem() {
      if (document.body.contains(targetElem)) {
        targetElem.focus();
      } else {
        setTimeout(waitTargetElem, 100);
      }
    }, 100);
  }

  onChange(event: any) {
    let index = String(event.selectedIndex);
    this.targetInput = 'input' + index;
    this.setFocus();
  }
}



回答2:


I have created my own alternate version to the one proposed by Narm. The problem that I had was that had rendering of actual stepper was postponed (eg. loading data from the server) ngAfterViewInit fired too soon. To combat that I am setting focus after step animation has finished. It creates slight delay but allows component to settle. StackBlitz and the code:

export class StepperOverviewExample implements OnInit {
  isLinear = false;
  firstFormGroup: FormGroup;
  secondFormGroup: FormGroup;
  private targetId = 'input0';

  constructor(private _formBuilder: FormBuilder) {}

  ngOnInit() {
    this.firstFormGroup = this._formBuilder.group({
      firstCtrl: ['', Validators.required]
    });
    this.secondFormGroup = this._formBuilder.group({
      secondCtrl: ['', Validators.required]
    });
  }

  setFocus() {
    const targetElem = document.getElementById(this.targetId);
    targetElem.focus();
  }

  setTargetId(event: any) {
    this.targetId = `input${event.selectedIndex}`;
  }
}
<mat-horizontal-stepper  (animationDone)="setFocus()" (selectionChange)="setTargetId($event)" [linear]="isLinear" #stepper>
  <mat-step [stepControl]="firstFormGroup">
    <form [formGroup]="firstFormGroup">
      <ng-template matStepLabel>Fill out your name</ng-template>
      <mat-form-field>
        <input id="input0" matInput placeholder="Last name, First name" formControlName="firstCtrl" required>
      </mat-form-field>
      <div>
        <button mat-button matStepperNext>Next</button>
      </div>
    </form>
  </mat-step>
  <mat-step [stepControl]="secondFormGroup">
    <form [formGroup]="secondFormGroup">
      <ng-template matStepLabel>Fill out your address</ng-template>
      <mat-form-field>
        <input id="input1" matInput placeholder="Address" formControlName="secondCtrl" required>
      </mat-form-field>
      <div>
        <button mat-button matStepperPrevious>Back</button>
        <button mat-button matStepperNext>Next</button>
      </div>
    </form>
  </mat-step>
  <mat-step>
    <ng-template matStepLabel>Done</ng-template>
    You are now done.
    <div>
      <button mat-button matStepperPrevious>Back</button>
      <button mat-button (click)="stepper.reset()">Reset</button>
    </div>
  </mat-step>
</mat-horizontal-stepper>

I think it still would be good to get rid of document for server side rendering compatibility.




回答3:


The solution with input ids and selectionChange listener works and you can add also (animationDone) listener so you can call setFocus() without setTimeout() logic.



来源:https://stackoverflow.com/questions/49011701/focus-input-after-going-to-the-next-step-in-matstepper

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!