Встановіть фокус на елементі <input>


101

Я працюю з інтерфейсною програмою з Angular 5, і мені потрібно приховати поле пошуку, але після натискання кнопки вікно пошуку повинно відображатися та фокусуватися.

Я спробував кілька способів, знайдених на StackOverflow з директивою, або близько того, але не можу досягти успіху.

Ось зразок коду:

@Component({
   selector: 'my-app',
   template: `
    <div>
    <h2>Hello</h2>
    </div>
    <button (click) ="showSearch()">Show Search</button>
    <p></p>
    <form>
      <div >
        <input *ngIf="show" #search type="text"  />            
      </div>
    </form>
    `,
  })
  export class App implements AfterViewInit {
  @ViewChild('search') searchElement: ElementRef;

  show: false;
  name:string;
  constructor() {    
  }

  showSearch(){
    this.show = !this.show;    
    this.searchElement.nativeElement.focus();
    alert("focus");
  }

  ngAfterViewInit() {
    this.firstNameElement.nativeElement.focus();
  }

Поле пошуку не встановлено для фокусування.

Як я можу це зробити?

Відповіді:


120

Змініть метод пошуку шоу таким чином

showSearch(){
  this.show = !this.show;  
  setTimeout(()=>{ // this will make the execution after the above boolean has changed
    this.searchElement.nativeElement.focus();
  },0);  
}

14
Чому нам потрібно використовувати setTimeout? Хіба зміна логічного значення не є синхронною?
Андрій Росу,

5
хіба це не плутається з zonejs?
lawphotog

1
@AndreiRosu, без нього я отримав помилку, оскільки зміни не були зроблені
Іслам Q.

11
Це працює, але без чіткого пояснення це магія. Магія часто ламається.
Джон Вайт

1
Я намагався сфокусувати свій елемент всередині панелі, коли панель була відкрита, тому тайм-аут 0 для мене не спрацював, але це зробило:setTimeout(() => { this.searchElement.nativeElement.focus() }, 100)
tclark333

42

Для цього слід використовувати html- автофокус :

<input *ngIf="show" #search type="text" autofocus /> 

Примітка. Якщо ваш компонент зберігається і використовується повторно, він автоматично фокусується лише при першому приєднанні фрагмента. Це можна подолати, якщо мати глобальний прослуховувач dom, який перевіряє наявність атрибута автофокусування всередині фрагмента dom, коли він прикріплений, а потім повторно застосовує його або фокусує за допомогою javascript.


42
Це спрацює лише раз на кожне оновлення сторінки, а не кілька разів.
moritzg

22

Ця директива миттєво фокусує і виділяє будь-який текст у елементі, як тільки він відображається. У деяких випадках для цього може знадобитися setTimeout, він не тестувався багато.

import { Directive, ElementRef, OnInit } from '@angular/core';

@Directive({
  selector: '[appPrefixFocusAndSelect]',
})
export class FocusOnShowDirective implements OnInit {

  constructor(private el: ElementRef) {
    if (!el.nativeElement['focus']) {
      throw new Error('Element does not accept focus.');
    }
  }

  ngOnInit(): void {
    const input: HTMLInputElement = this.el.nativeElement as HTMLInputElement;
    input.focus();
    input.select();
  }
}

І в HTML:

 <mat-form-field>
     <input matInput type="text" appPrefixFocusAndSelect [value]="'etc'">
 </mat-form-field>

дякую, це єдине рішення, яке спрацювало для мене у випадку очікування на http-запит
Абдельхалім ФЕЛЛАГА ЧЕБРА

Я вважаю, що це рішення є кращим, ніж прийнята відповідь. Він чистіший у випадку повторного використання коду та більш орієнтований на кут.
derekbaker783

9

Я збираюся зважити це (Рішення Angular 7)

input [appFocus]="focus"....
import {AfterViewInit, Directive, ElementRef, Input,} from '@angular/core';

@Directive({
  selector: 'input[appFocus]',
})
export class FocusDirective implements AfterViewInit {

  @Input('appFocus')
  private focused: boolean = false;

  constructor(public element: ElementRef<HTMLElement>) {
  }

  ngAfterViewInit(): void {
    // ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked.
    if (this.focused) {
      setTimeout(() => this.element.nativeElement.focus(), 0);
    }
  }
}

1
Здається, це майже ідентично прийнятій відповіді
Ліам,

8

Це працює в Angular 8 без setTimeout:

import {AfterContentChecked, Directive, ElementRef} from '@angular/core';

@Directive({
  selector: 'input[inputAutoFocus]'
})
export class InputFocusDirective implements AfterContentChecked {
  constructor(private element: ElementRef<HTMLInputElement>) {}

  ngAfterContentChecked(): void {
    this.element.nativeElement.focus();
  }
}

Пояснення: Добре, отже, це працює через: Виявлення змін. З тієї ж причини працює setTimout, але під час запуску setTimeout в Angular він буде обходити Zone.js і запускати всі перевірки знову, і це працює, тому що, коли setTimeout завершено, всі зміни завершені. За допомогою правильного гачка життєвого циклу (AfterContentChecked) можна досягти того самого результату, але з тією перевагою, що додатковий цикл не буде запущений. Функція запускається, коли всі зміни перевіряються та передаються, і запускається після гачків AfterContentInit та DoCheck. Якщо я помиляюся тут, будь ласка, виправте мене.

Більше одного життєвого циклу та виявлення змін на https://angular.io/guide/lifecycle-hooks

ОНОВЛЕННЯ : Я знайшов ще кращий спосіб зробити це, якщо використовую Angular Material CDK, пакет a11y. Спочатку імпортуйте A11yModule в модуль, де оголошуєте компонент, у якому є поле введення. Потім використовуйте директиви cdkTrapFocus та cdkTrapFocusAutoCapture і використовуйте, як це, у html та встановіть tabIndex на вхід:

<div class="dropdown" cdkTrapFocus cdkTrapFocusAutoCapture>
    <input type="text tabIndex="0">
</div>

У нас були деякі проблеми з випадаючими списками щодо позиціонування та швидкості реагування, і ми замість цього почали використовувати OverlayModule з cdk, і цей метод за допомогою A11yModule працює бездоганно.


Привіт ... Я не пробую cdkTrapFocusAutoCaptureатрибут, але я змінив ваш перший приклад на свій код. З невідомих (для мене) причин він не працював із гачком життєвого циклу AfterContentChecked, а лише з OnInit. В частковості з AfterContentChecked я не можу змінити фокус і перемістити (за допомогою миші або клавіатури) на інший вхід без цієї директиви в тій же формі.
timhecker

@timhecker так, ми зіткнулися з подібною проблемою при перенесенні наших компонентів на накладення cdk (він працював, коли використовував лише css замість накладання для позиціонування). Він фокусувався на невизначений час, коли компонент, що використовує директиву, був видимим. Єдиним рішенням, яке я знайшов, було використання директив cdk, згаданих в оновленні.
SNDVLL

6

Щоб виконати виконання після зміни логічного значення та уникнути використання тайм-ауту, ви можете зробити:

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

constructor(private cd: ChangeDetectorRef) {}

showSearch(){
  this.show = !this.show;  
  this.cd.detectChanges();
  this.searchElement.nativeElement.focus();
}

5
Я спробував це з кутовим 7, це не спрацювало, використання тайм-ауту спрацювало нормально
rekiem87

для мене також у кутовому 8 не працює, до поганого ми повинні повернутися до setTimeout
Андрій

6

У Angular, у самому HTML, ви можете встановити фокус для введення натисканням кнопки.

<button (click)="myInput.focus()">Click Me</button>

<input #myInput></input>

Ця відповідь показує простий спосіб програмного вибору іншого елемента в HTML, що я шукав (усі відповіді "початкового фокусу" не вирішують, як реагувати на подію, змінивши фокус) - на жаль, мені потрібно було реагувати до mat-select selectionChangedподії, яка трапляється перед іншими матеріалами інтерфейсу, тому фокусування в той момент не спрацювало. Замість цього я повинен був написати метод setFocus(el:HTMLElement):void { setTimeout(()=>el.focus(),0); }і викликати його з обробника подій: <mat-select (selectionChanged)="setFocus(myInput)">. Не такий приємний, але простий і добре працює. Дякую!
Guss



0

html компонента:

<input [cdkTrapFocusAutoCapture]="show" [cdkTrapFocus]="show">

контролер компонента:

showSearch() {
  this.show = !this.show;    
}

..і не забувайте про імпорт A11yModule з @ angular / cdk / a11y

import { A11yModule } from '@angular/cdk/a11y'

-1

Це також простіше зробити.

let elementReference = document.querySelector('<your css, #id selector>');
    if (elementReference instanceof HTMLElement) {
        elementReference.focus();
    }

3
Ви насправді не хочете безпосередньо запитувати dom.
Річард, Девенпорт,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.