Ustaw fokus na elemencie <input>

108

Pracuję z aplikacją front-end z Angular 5 i muszę mieć ukryte pole wyszukiwania, ale po kliknięciu przycisku pole wyszukiwania powinno być wyświetlane i wyostrzone.

Wypróbowałem kilka sposobów znalezionych w StackOverflow z dyrektywą lub tak, ale nie mogę się powieść.

Oto przykładowy kod:

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

Pole wyszukiwania nie jest aktywne.

Jak mogę to zrobić?

Pion
źródło

Odpowiedzi:

128

Zmodyfikuj metodę wyszukiwania pokazów w ten sposób

showSearch(){
  this.show = !this.show;  
  setTimeout(()=>{ // this will make the execution after the above boolean has changed
    this.searchElement.nativeElement.focus();
  },0);  
}
Arvind Muthuraman
źródło
15
Dlaczego musimy używać setTimeout? Czy zmiana wartości boolowskiej nie jest synchroniczna?
Andrei Rosu
6
czy to nie zadziera z zonejs?
lawphotog
1
@AndreiRosu, bez tego otrzymywałem błąd, ponieważ zmiany nie zostały wyrenderowane
Islam Pytanie
13
To działa, ale bez jasnego wyjaśnienia to magia. Magia często się psuje.
John White,
1
Próbowałem skupić swój element wewnątrz panelu, gdy panel był otwarty, więc limit czasu 0 nie działał dla mnie, ale to setTimeout(() => { this.searchElement.nativeElement.focus() }, 100)
zadziałało
41

Powinieneś użyć do tego autofokusa html :

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

Uwaga: jeśli twój komponent jest utrwalony i ponownie użyty, będzie automatycznie ustawiał ostrość tylko przy pierwszym dołączeniu fragmentu. Można temu zaradzić, mając globalny odbiornik domeny, który sprawdza atrybut autofocus wewnątrz fragmentu dom, gdy jest on dołączony, a następnie ponownie go stosuje lub aktywuje za pomocą javascript.

Zjadł
źródło
47
Działa to tylko raz na każde odświeżenie strony, a nie wielokrotnie.
moritzg,
23

Ta dyrektywa natychmiast skupi się i zaznaczy dowolny tekst w elemencie, gdy tylko zostanie wyświetlony. W niektórych przypadkach może to wymagać setTimeout, nie zostało to zbytnio przetestowane.

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

A w HTML:

 <mat-form-field>
     <input matInput type="text" appPrefixFocusAndSelect [value]="'etc'">
 </mat-form-field>
ggranum
źródło
dziękuję, to jedyne rozwiązanie, które u mnie zadziałało w przypadku oczekiwania na żądanie http
Abdelhalim FELLAGUE CHEBRA
1
Uważam, że to rozwiązanie jest bardziej korzystne niż zaakceptowana odpowiedź. Jest czystszy w przypadku ponownego wykorzystania kodu i bardziej skoncentrowany na Angular.
derekbaker783
Nie zapomnij również dodać dyrektywy w deklaracjach modułu.
Elijah Dollin
11

Mam zamiar rozważyć to (rozwiązanie 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);
    }
  }
}
Ricardo Saracino
źródło
1
Wydaje się, że jest to prawie identyczne z zaakceptowaną odpowiedzią
Liam
9

To działa i Angular 8 bez 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();
  }
}

Wyjaśnienie: OK, więc to działa z powodu: Wykrywania zmian. To ten sam powód, dla którego działa setTimout, ale po uruchomieniu setTimeout w Angular ominie Zone.js i uruchomi wszystkie sprawdzenia ponownie, i działa, ponieważ po zakończeniu setTimeout wszystkie zmiany są zakończone. Przy prawidłowym haku cyklu życia (AfterContentChecked) można osiągnąć ten sam wynik, ale z tą zaletą, że dodatkowy cykl nie zostanie uruchomiony. Funkcja zostanie uruchomiona, gdy wszystkie zmiany zostaną sprawdzone i przekazane, i zostanie uruchomiona po zaczepach AfterContentInit i DoCheck. Jeśli się mylę, popraw mnie.

Więcej cykli życia i wykrywanie zmian na https://angular.io/guide/lifecycle-hooks

AKTUALIZACJA : Znalazłem jeszcze lepszy sposób, aby to zrobić, jeśli używa się Angular Material CDK, pakietu a11y. Najpierw zaimportuj A11yModule do modułu deklarując komponent, w którym masz pole wejściowe. Następnie użyj dyrektyw cdkTrapFocus i cdkTrapFocusAutoCapture i użyj w ten sposób w html i ustaw tabIndex na wejściu:

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

Mieliśmy pewne problemy z naszymi listami rozwijanymi dotyczącymi pozycjonowania i responsywności i zamiast tego zaczęliśmy używać OverlayModule z cdk, a ta metoda wykorzystująca A11yModule działa bez zarzutu.

SNDVLL
źródło
Cześć ... Nie próbuję cdkTrapFocusAutoCaptureatrybutu, ale zmieniłem Twój pierwszy przykład na mój kod. Z nieznanych (dla mnie) powodów nie działał z zaczepem cyklu życia AfterContentChecked, ale tylko z OnInit. W szczególności z AfterContentChecked nie mogę zmienić fokusa i przenieść (za pomocą myszy lub klawiatury) na inne wejście bez tej dyrektywy w tej samej formie.
Timhecker
@timhecker tak, napotkaliśmy podobny problem podczas migracji naszych komponentów do nakładki cdk (zadziałało, gdy do pozycjonowania używaliśmy tylko css zamiast nakładki). Skupiał się w nieskończoność na danych wejściowych, gdy komponent używający dyrektywy był widoczny. Jedynym rozwiązaniem, które znalazłem, było użycie dyrektyw cdk wymienionych w aktualizacji.
SNDVLL
7

W Angular, w samym HTML, możesz ustawić fokus na wprowadzanie po kliknięciu przycisku.

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

<input #myInput></input>
shaheer shukur
źródło
Ta odpowiedź pokazuje prosty sposób na programowe wybranie innego elementu w HTML, czyli tego, czego szukałem (wszystkie odpowiedzi "początkowego fokusa" nie rozwiązują tego, jak zareagować na zdarzenie poprzez zmianę fokusu) - niestety musiałem zareagować do mat-select selectionChangedwydarzenia, które ma miejsce przed innymi elementami interfejsu użytkownika, więc przyciągnięcie uwagi w tym momencie nie zadziałało. Zamiast tego musiałem napisać metodę setFocus(el:HTMLElement):void { setTimeout(()=>el.focus(),0); }i wywołać ją z obsługi zdarzeń: <mat-select (selectionChanged)="setFocus(myInput)">. Nie tak ładne, ale proste i działa dobrze. dzięki!
Guss
Nie działa dla mnie. Otrzymuję ERROR TypeError: Cannot read property 'focus' of undefined
Reza Taba
@RezaTaba, czy wywołałeś funkcję „myInput.focus ()” czy po prostu napisałeś „myInput.focus”? Upewnij się również, że element wejściowy znajduje się wewnątrz renderowanego elementu div (na przykład * ngIf false może spowodować błąd)
shaheer shukur
6

Aby wykonać wykonanie po zmianie wartości logicznej i uniknąć wykorzystania limitu czasu, możesz wykonać:

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

constructor(private cd: ChangeDetectorRef) {}

showSearch(){
  this.show = !this.show;  
  this.cd.detectChanges();
  this.searchElement.nativeElement.focus();
}
Marcin Restel
źródło
5
Próbowałem tego z kątowym 7, nie działało, użycie limitu czasu działało dobrze
rekiem87
u mnie też w kątowej 8 nie działa, szkoda musimy wrócić do setTimeout
Andrei Chivu
1

Tylko przy użyciu szablonu kątowego

<input type="text" #searchText>

<span (click)="searchText.focus()">clear</span>
Sandipan Mitra
źródło
„Jeśli używasz materiału ...”
Andrew Koper
ERROR TypeError: Cannot read property 'focus' of undefined
Reza Taba
1

html komponentu:

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

kontroler komponentu:

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

.. i nie zapomnij o imporcie A11yModule z @ angular / cdk / a11y

import { A11yModule } from '@angular/cdk/a11y'
Cichy
źródło
Najlepsze rozwiązanie. Dzięki. Inne rozwiązania dały mi błąd.
Reza Taba
1

Mam ten sam scenariusz, to zadziałało dla mnie, ale nie mam funkcji „ukryj / pokaż”, którą masz. Więc może mógłbyś najpierw sprawdzić, czy skupiasz się, gdy masz zawsze widoczne pole, a następnie spróbować rozwiązać, dlaczego nie działa, gdy zmienisz widoczność (prawdopodobnie dlatego musisz zastosować sen lub obietnicę)

Aby ustawić fokus, to jedyna zmiana, którą musisz zrobić:

Twoje dane wejściowe HTML powinny wyglądać następująco:

<input #yourControlName matInput>

w swojej klasie TS, odwołaj się w ten sposób w sekcji zmiennych (

export class blabla...
    @ViewChild("yourControlName") yourControl : ElementRef;

Twój przycisk jest w porządku, dzwonię:

  showSearch(){
       ///blabla... then finally:
       this.yourControl.nativeElement.focus();
}

i to wszystko. Możesz sprawdzić to rozwiązanie w tym poście, które znalazłem, a więc dzięki -> https://codeburst.io/focusing-on-form-elements-the-angular-way-e9a78725c04f

Yogurtu
źródło
-1

Łatwiej też jest to zrobić.

let elementReference = document.querySelector('<your css, #id selector>');
    if (elementReference instanceof HTMLElement) {
        elementReference.focus();
    }
patz
źródło
3
Naprawdę nie chcesz bezpośrednio sprawdzać domeny.
Richard.Davenport