Setzen Sie den Fokus auf das <input> -Element

101

Ich arbeite an einer Front-End-Anwendung mit Angular 5 und muss ein Suchfeld ausblenden, aber auf Knopfdruck sollte das Suchfeld angezeigt und fokussiert werden.

Ich habe einige Möglichkeiten ausprobiert, die in StackOverflow mit Direktive oder so gefunden wurden, kann aber keinen Erfolg haben.

Hier ist der Beispielcode:

@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();
  }

Das Suchfeld ist nicht auf Fokus eingestellt.

Wie kann ich das machen?

Bob
quelle

Antworten:

121

Ändern Sie die Suchmethode für Shows wie folgt

showSearch(){
  this.show = !this.show;  
  setTimeout(()=>{ // this will make the execution after the above boolean has changed
    this.searchElement.nativeElement.focus();
  },0);  
}
Arvind Muthuraman
quelle
14
Warum müssen wir setTimeout verwenden? Ist die Änderung des Booleschen Wertes nicht synchron?
Andrei Rosu
5
macht das nichts mit zonejs zu tun?
Lawphotog
1
@AndreiRosu, ohne es bekam ich einen Fehler, weil die Änderungen nicht gerendert wurden
Islam Q.
11
Das funktioniert, aber ohne eine klare Erklärung ist es Magie. Magie bricht oft.
John White
1
Ich habe versucht, mein Element innerhalb eines Panels zu fokussieren, als das Panel geöffnet wurde, daher funktionierte das Zeitlimit 0 für mich nicht, aber dies tat:setTimeout(() => { this.searchElement.nativeElement.focus() }, 100)
tclark333
41

Sie sollten dafür den HTML- Autofokus verwenden :

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

Hinweis: Wenn Ihre Komponente beibehalten und wiederverwendet wird, wird der Autofokus nur beim ersten Anhängen des Fragments automatisch aktiviert. Dies kann überwunden werden, indem ein globaler Dom-Listener über ein Autofokus-Attribut in einem Dom-Fragment verfügt, wenn es angehängt ist, und es dann erneut anwendet oder über Javascript fokussiert.

N-aß
quelle
42
Dies funktioniert nur einmal bei jeder Seitenaktualisierung, nicht mehrmals.
Moritzg
22

Diese Anweisung fokussiert und wählt sofort jeden Text im Element aus, sobald er angezeigt wird. Dies kann in einigen Fällen ein setTimeout erfordern, es wurde nicht viel getestet.

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();
  }
}

Und im HTML:

 <mat-form-field>
     <input matInput type="text" appPrefixFocusAndSelect [value]="'etc'">
 </mat-form-field>
ggranum
quelle
Vielen Dank, dies ist die einzige Lösung, die für mich im Falle des Wartens auf http-Anfrage
funktioniert hat
Ich finde diese Lösung vorzuziehen als die akzeptierte Antwort. Es ist sauberer bei der Wiederverwendung von Code und winkelzentrierter.
derekbaker783
9

Ich werde das abwägen (Angular 7 Solution)

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);
    }
  }
}
Ricardo Saracino
quelle
1
Dies scheint ziemlich identisch mit der akzeptierten Antwort zu sein
Liam
8

Dies funktioniert in Angular 8 ohne 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();
  }
}

Erläuterung: Ok, das funktioniert aus folgenden Gründen: Änderungserkennung. Es ist der gleiche Grund, warum setTimout funktioniert, aber wenn ein setTimeout in Angular ausgeführt wird, umgeht es Zone.js und führt alle Überprüfungen erneut aus. Wenn das setTimeout abgeschlossen ist, werden alle Änderungen abgeschlossen. Mit dem richtigen Lifecycle Hook (AfterContentChecked) kann das gleiche Ergebnis erzielt werden, jedoch mit dem Vorteil, dass der zusätzliche Zyklus nicht ausgeführt wird. Die Funktion wird ausgelöst, wenn alle Änderungen überprüft und übergeben wurden, und wird nach den Hooks AfterContentInit und DoCheck ausgeführt. Wenn ich mich hier irre, korrigiere mich bitte.

Weitere Lebenszyklen und Änderungserkennung unter https://angular.io/guide/lifecycle-hooks

UPDATE : Ich habe einen noch besseren Weg gefunden, dies zu tun, wenn man Angular Material CDK verwendet, das a11y-Paket. Importieren Sie zuerst A11yModule in das Modul und deklarieren Sie die Komponente, in der Sie das Eingabefeld haben. Verwenden Sie dann die Direktiven cdkTrapFocus und cdkTrapFocusAutoCapture und verwenden Sie diese in HTML und setzen Sie tabIndex für die Eingabe:

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

Wir hatten einige Probleme mit unseren Dropdowns in Bezug auf Positionierung und Reaktionsfähigkeit und haben stattdessen begonnen, das OverlayModule aus dem CDK zu verwenden. Diese Methode mit A11yModule funktioniert einwandfrei.

SNDVLL
quelle
Hallo ... Ich versuche kein cdkTrapFocusAutoCaptureAttribut, aber ich habe Ihr erstes Beispiel in meinen Code geändert. Aus unbekannten Gründen (für mich) funktionierte es nicht mit AfterContentChecked Lifecycle Hook, sondern nur mit OnInit. Insbesondere mit AfterContentChecked kann ich den Fokus nicht ändern und (mit Maus oder Tastatur) ohne diese Anweisung in derselben Form auf einen anderen Eingang wechseln.
Timhecker
@timhecker Ja, wir hatten ein ähnliches Problem bei der Migration unserer Komponenten auf das CDK-Overlay (es funktionierte, wenn nur CSS anstelle eines Overlays für die Positionierung verwendet wurde). Es konzentrierte sich auf unbestimmte Zeit auf die Eingabe, wenn eine Komponente, die die Direktive verwendet, sichtbar war. Die einzige Lösung, die ich gefunden habe, war die Verwendung der im Update genannten cdk-Anweisungen.
SNDVLL
6

So führen Sie die Ausführung durch, nachdem sich der Boolesche Wert geändert hat, und vermeiden die Verwendung von Timeout:

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

constructor(private cd: ChangeDetectorRef) {}

showSearch(){
  this.show = !this.show;  
  this.cd.detectChanges();
  this.searchElement.nativeElement.focus();
}
Marcin Restel
quelle
5
Ich habe dies mit Winkel 7 versucht, es hat nicht funktioniert, mit Timeout hat gut funktioniert
rekiem87
für mich funktioniert auch in eck 8 nicht, schade, dass wir zurück zu setTimeout
Andrei Chivu
5

In Angular können Sie in HTML selbst den Fokus so einstellen, dass er beim Klicken auf eine Schaltfläche eingegeben wird.

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

<input #myInput></input>
shaheer shukur
quelle
Diese Antwort zeigt eine einfache Möglichkeit, programmgesteuert ein anderes Element in HTML auszuwählen, nach dem ich gesucht habe (alle Antworten mit "Anfangsfokus" lösen nicht, wie man auf ein Ereignis reagiert, indem man den Fokus ändert) - leider musste ich reagieren zu einem mat-select selectionChangedEreignis, das vor anderen UI-Dingen auftritt, sodass das Ziehen des Fokus an diesem Punkt nicht funktioniert hat. Stattdessen musste ich eine Methode schreiben setFocus(el:HTMLElement):void { setTimeout(()=>el.focus(),0); }und sie vom Event-Handler aufrufen : <mat-select (selectionChanged)="setFocus(myInput)">. Nicht so schön, aber einfach und funktioniert gut. Danke!
Guss
1

Nur Angular Template verwenden

<input type="text" #searchText>

<span (click)="searchText.focus()">clear</span>
Sandipan Mitra
quelle
0

HTML der Komponente:

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

Controller der Komponente:

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

..und vergessen Sie nicht, A11yModule aus @ angle / cdk / a11y zu importieren

import { A11yModule } from '@angular/cdk/a11y'
Cichy
quelle
-1

Einfacher ist es auch, dies zu tun.

let elementReference = document.querySelector('<your css, #id selector>');
    if (elementReference instanceof HTMLElement) {
        elementReference.focus();
    }
patz
quelle
3
Sie möchten den Dom wirklich nicht direkt abfragen.
Richard.Davenport