Home > OS >  Angular set focus to select element on page load
Angular set focus to select element on page load

Time:02-23

I can set focus to an input field, but not a select. What am I missing?

This works:

    <input matInput formControlName="name" required maxlength="100" id="elementFocus" appElementFocus="true" />

This does not

    <mat-select formControlName="countryId" required id="elementFocus" appElementFocus="true">

Here's the entire section that exists now, and when the page loads, the 2nd form element (Name) has focus. I need the select to have focus.

    <mat-card-content>

      <div fxLayout="column" fxLayoutGap="25px" >

        
        <mat-form-field appearance="standard">
          <mat-label>Countries</mat-label>
          <mat-select formControlName="countryId" #countrySelectRef required>
            <mat-option *ngFor="let c of countries" [value]="c.id"
              >{{ c.name }}
            </mat-option>
          </mat-select>
          <mat-error *ngIf="hasError(f.countryId)">{{
            getErrorMessage(f.countryId)
          }}</mat-error>
        </mat-form-field>
        
        <mat-form-field appearance="standard">
          <mat-label>Name</mat-label>
          <input matInput formControlName="name" required maxlength="100" />
          <mat-hint align="end">{{ f.name.value.length }} / 100</mat-hint>
          <mat-error *ngIf="hasError(f.name)">{{
            getErrorMessage(f.name)
          }}</mat-error>
        </mat-form-field>
      </div>

    </mat-card-content>

.ts code (narrowed down to all that is relevant, I think).


  @ViewChild(MatSelect) countrySelectRef: MatSelect;

  constructor(
    private formBuilder: FormBuilder,
    private activatedRoute: ActivatedRoute,
    private router: Router,
    private matDialog: MatDialog,
    private readonly countryService: CountryService,
    private readonly stateService: StateService,
    private messageService: UIMessageService) { }

  ngOnInit(): void {
    const paramId = this.getParam('id');
    this.isAdd = !paramId
    this.id = !paramId ? uuid() : paramId
    this.heading = this.isAdd ? `Add ${this.headingName}` : `Edit ${this.headingName}`
    this.initForm();
    if (!this.isAdd) {
      this.patchForm()
    }
  }
  

  ngAfterViewInit(): void {

    if (this.countrySelectRef) {
      this.countrySelectRef.focus();
    }
  }

CodePudding user response:

Your code has a problem, in the sense that it can trigger a ExpressionChangedAfterItHasBeenCheckedError error, but even with that error, it should work fine. Your code and the error message, described in your comment, don't match up and may indicate that the issue is elsewhere.

I do provide an alternative, which hopefully, will solve your problem. You need to add the A11yModule to the imports array of the module that declare the component that use that template.

Template

<mat-form-field appearance="standard" cdkMonitorSubtreeFocus>
    <mat-label>Countries</mat-label>
    <mat-select formControlName="countryId" #countrySelectRef required
    cdkTrapFocusAutoCapture
    cdkTrapFocus="false">
        <mat-option *ngFor="let c of countries" [value]="c.id">
            {{ c.name }}
        </mat-option>
    </mat-select>
    <mat-error *ngIf="hasError(f.countryId)">
        {{ getErrorMessage(f.countryId) }}
    </mat-error>
</mat-form-field>

Note the 2 (with 3 inputs in total) directives added:

  • cdkMonitorSubtreeFocus: considers an element focused if it or any of its children are focused
  • cdkTrapFocusAutoCapture: Whether the directive should automatically move focus into the trapped region upon initialization and return focus to the previous activeElement upon destruction.
  • cdkTrapFocus="false": If this value is true, it will trap the focus on that directive indefinitely.

Ref: Material documentation

  • Related