dloewen
dloewen

Reputation: 995

Angular: How do you focus an input inside a ngbDropdown Menu?

I'm trying to use an ElementRef to focus on an input. This works fine except for inside a dropdown from the ng-bootstrap library. How do you trigger the event in a ngbDropdown?

Example: https://stackblitz.com/edit/angular-z7q9xm

component class

import { Component, ViewChild, ElementRef } from '@angular/core';

@Component({
  selector: 'ngbd-dropdown-basic',
  templateUrl: './dropdown-basic.html'
})
export class NgbdDropdownBasic {
  @ViewChild("search") searchField: ElementRef;
  @ViewChild("searchOutside") searchFieldOutside: ElementRef;

  onToggle(dropDownOpen: boolean) {
    if (dropDownOpen) {
      this.searchField.nativeElement.focus();
    }
  }

  focusOtherField() {
    this.searchFieldOutside.nativeElement.focus();
  }
}

component template

<div class="row">
  <div class="col"><h3>Example 1: Inside dropdown <br>(not working)</h3></div>
</div>
<div class="row form-group">
  <div class="col">
    <div ngbDropdown (openChange)="onToggle($event)" class="d-inline-block">
      <button class="btn btn-outline-primary" id="dropdownBasic1" ngbDropdownToggle>Toggle dropdown</button>
      <div ngbDropdownMenu aria-labelledby="dropdownBasic1">
        <div class="p-2"><input #search name="search" class="form-control" placeholder="Search..."></div>
      </div>
    </div>
  </div>
</div>
<hr>
<div class="row">
  <div class="col"><h3>Example 2: Outside dropdown</h3></div>
</div>
<div class="row form-group">
  <div class="col-auto"><button (click)="focusOtherField()" type="button" class="btn btn-outline-primary">Focus this field:</button></div>
    <div class="col">
    <input #searchOutside name="searchOutside" class="form-control" placeholder="Search...">
  </div>
</div>

Upvotes: 3

Views: 2571

Answers (1)

Ian A
Ian A

Reputation: 6128

The problem is that at the point of clicking on the dropdown and calling your onToggle, the DOM has not yet been updated to render the dropdown and the input within the dropdown.

What you can do is inject a ChangeDetectorRef into the component via the constructor:

import { ChangeDetectorRef } from '@angular/core';
...

export class NgbdDropdownBasic {
  ...
  constructor(private _cdRef: ChangeDetectorRef) {}

and call this._cdRef.detectChanges(); before you focus on the input in your onToggle method:

onToggle(dropDownOpen: boolean) {
  if (dropDownOpen) {
    this._cdRef.detectChanges();
    this.searchField.nativeElement.focus();
  }
}

This will result in the dropdown and input being rendered, so that when you call this.searchField.nativeElement.focus(); it is already visible and will get the focus.

Please see this StackBlitz for a working demo.

Upvotes: 4

Related Questions