Angular Material 将初始焦点设置在没有点击事件的表单控件上

Angular Material set initial focus on form control without click event

我正在使用 Angular 9 和 Material。我有一个步进器,可以指导用户完成我们的工作流程的步骤。

我需要用户能够完全使用键盘完成这些步骤,而无需单击控件。我见过很多通过点击事件将焦点设置在控件上的例子。我不希望用户必须单击按钮才能将焦点设置在第一个文本区域。 如何在步进器初始化时用户无需单击任何内容而获得文本区域的初始焦点?我尝试在 ngAfterViewInit 中使用元素引用,但它不起作用.任何人都可以帮助我了解如何执行此操作吗?谢谢

这是我的 HTML:

<pseudo-common-tool-header title="{{this.title}}"></pseudo-common-tool-header>
<div class="pseudo-content-manager-tool-area">
<mat-horizontal-stepper #stepper>
<mat-step [stepControl]="formGroup">
    <form [formGroup]="formGroup">
        <ng-template matStepLabel>Workstation Ids</ng-template>
        <div>
            <mat-form-field appearance="outline" class="eris-width-45" >
            <mat-label>List of Old/New Workstations to swap (one pair per line)</mat-label>
            <textarea #wkstns rows="8" #workstations matInput placeholder="Old Workstation, New Workstation" formControlName="workstations" required></textarea>
            </mat-form-field>
        </div>
        <div>
            <mat-form-field appearance="outline" >
                <mat-label>Soft Delete Date</mat-label>
                <input matInput #softDate [min]="minDate" [matDatepicker]="picker3" formControlName="removalDate">
                        <mat-datepicker-toggle matSuffix [for]="picker3"></mat-datepicker-toggle>
                        <mat-datepicker #picker3 color="primary"></mat-datepicker>
            </mat-form-field>
        </div>
        <div>
            <button color="primary" #step1next mat-raised-button matStepperNext>Next</button>
        </div>
    </form>
</mat-step>
<mat-step  label="Validate">
    <div>
        <button mat-raised-button color="secondary" matStepperPrevious>Back</button>
        <button mat-raised-button color="secondary" matStepperNext>Next</button>
      </div>
</mat-step>
<mat-step>
    <ng-template matStepLabel>Epic Build</ng-template>
</mat-step>

这是我的组件:

@Component({
 templateUrl: './swap-workstations.component.html',
 styleUrls: ['./swap-workstations.component.scss']
  })

 @PseudoTool({
 title: 'Check Workstation Existence in Epic',
 componentName: 'CheckWorkstationExistenceInEpicComponent'
  })

  export class SwapWorkstationsComponent extends BasePseudoTool implements OnInit {
       formGroup: FormGroup;
    // minimum date for date picker
    minDate: Date;
 @ViewChild('wkstns') wkstns: ElementRef;
 @ViewChild('softDate') softDate: ElementRef;
 @ViewChild('step1next') step1next: ElementRef;

 constructor(private formBuilder: FormBuilder) {
     super();
    }

 ngOnInit(): void {
   this.formGroup = this.formBuilder.group({
     workstations: ['', Validators.required],
     removalDate: ['', Validators.required]
   });

// Set minimum date for date pickers.
this.minDate = new Date();
 }

 ngViewAfterInit(): void {
   this.wkstns.nativeElement.focus();
 }   

}

不确定使用 setTimeout 这样做是否正确。它有点黑客,我可能是错的,但它确实有效。

ngViewAfterInit(): void {
   setTimeout(() => this.wkstns.nativeElement.focus());
 } 

理想情况下,您会使用 autofocus 属性,但它很不稳定,并不总是有效,所以我只是创建了一个指令来确保它有效。我在表单上使用它来将焦点设置在第一个元素上,以便用户可以使用键盘在表单中导航。

import { coerceBooleanProperty } from "@angular/cdk/coercion";
import { DOCUMENT } from "@angular/common";
import {
  AfterViewInit,
  Directive,
  ElementRef,
  Inject,
  Input
} from "@angular/core";

@Directive({
  selector: "[autofocus]"
})
export class AutofocusDirective implements AfterViewInit {
  private _host: HTMLElement;
  private _focused: Element | null;
  private _autofocus = true;

  @Input()
  set autofocus(value: boolean | string) {
    this._autofocus = coerceBooleanProperty(value);
  }

  constructor(
    private readonly element: ElementRef,
    @Inject(DOCUMENT) document: HTMLDocument
  ) {
    if (typeof this.element.nativeElement.focus !== "function") {
      throw new Error("Html element must be focusable");
    }
    this._host = element.nativeElement;
    this._focused = document.activeElement;
  }

  public ngAfterViewInit(): void {
    if (this._autofocus && this._host && this._host !== this._focused) {
      setTimeout(() => this.element.nativeElement.focus());
    }
  }
}

然后您只需将 autofocus 属性添加到文本区域即可。这样可以让您的组件更干净并且可以重复使用。