<input> öğesine odaklanın


101

Angular 5 ile bir ön uç uygulamasında çalışıyorum ve gizli bir arama kutusunun olması gerekiyor, ancak bir düğmeye tıklandığında arama kutusu görüntülenmeli ve odaklanmalıdır.

StackOverflow'da bulunan birkaç yolu veya benzeri bir yönerge ile denedim, ancak başaramıyorum.

İşte örnek 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();
  }

Arama kutusu odaklanmak için ayarlanmamış.

Bunu nasıl yapabilirim?

Yanıtlar:


120

Şov arama yöntemini şu şekilde değiştirin

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

14
Neden setTimeout kullanmamız gerekiyor? Boolean değişimi eşzamanlı değil mi?
Andrei Rosu

5
zonejs ile uğraşmaz mı?
lawphotog

1
@AndreiRosu, onsuz, değişiklikler yapılmadığı için bir hata alıyordum
İslam Q.

11
Bu işe yarıyor, ancak net bir açıklama olmadan sihir. Sihir sık ​​sık bozulur.
John White

1
Panel açıldığında setTimeout(() => { this.searchElement.nativeElement.focus() }, 100)
öğemi

42

Bunun için html otomatik odaklama kullanmalısınız :

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

Not: Bileşeniniz kalıcıysa ve yeniden kullanılırsa, yalnızca parça ilk takıldığında otomatik odaklanır. Bu, bir dom parçası eklendiğinde otomatik odaklama niteliğini kontrol eden ve ardından onu yeniden uygulayan veya javascript aracılığıyla odaklanan global bir etki alanı dinleyicisine sahip olarak aşılabilir.


42
Bu, birden çok kez değil, yalnızca her sayfa yenilemesinde bir kez çalışacaktır.
moritzg

22

Bu yönerge, görüntülendiği anda öğedeki herhangi bir metne anında odaklanacak ve seçecektir. Bu, bazı durumlarda bir setTimeout gerektirebilir, çok fazla test edilmemiştir.

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

Ve HTML'de:

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

teşekkür ederim, bu, http talebinin beklenmesi durumunda benim için işe yarayan tek çözümdür
Abdelhalim FELLAGUE CHEBRA

Bu çözümü kabul edilen cevaptan daha tercih edilebilir buluyorum. Kodun yeniden kullanılması durumunda daha temiz ve daha Açısal merkezli.
derekbaker783

9

Bunu tartacağım (Açısal 7 Çözümü)

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


8

Bu, setTimeout olmadan Angular 8 çalışıyor:

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

Açıklama: Tamam, bu nedenle bunun işe yaraması: Değişiklik algılaması. SetTimout'un çalışmasının nedeni budur, ancak Angular'da bir setTimeout çalıştırırken Zone.js'yi atlayacak ve tüm kontrolleri tekrar çalıştıracaktır ve setTimeout tamamlandığında tüm değişiklikler tamamlandığı için çalışır. Doğru yaşam döngüsü kancası (AfterContentChecked) ile aynı sonuca ulaşılabilir, ancak ekstra döngünün çalıştırılmaması avantajıyla elde edilebilir. İşlev, tüm değişiklikler kontrol edilip geçildiğinde çalışacak ve AfterContentInit ve DoCheck kancalarından sonra çalışacaktır. Burada yanılıyorsam lütfen beni düzeltin.

Https://angular.io/guide/lifecycle-hooks adresinde bir yaşam döngüsü ve değişiklik algılama

GÜNCELLEME : A11y paketi olan Angular Material CDK kullanılıyorsa bunu yapmanın daha da iyi bir yolunu buldum. Öncelikle , giriş alanına sahip olduğunuz bileşeni bildiren modüle A11yModule'ı içe aktarın . Ardından cdkTrapFocus ve cdkTrapFocusAutoCapture yönergelerini kullanın ve html'de bu şekilde kullanın ve girişte tabIndex'i ayarlayın:

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

Konumlandırma ve yanıt verme ile ilgili açılır menülerde bazı sorunlar yaşadık ve bunun yerine OverlayModule'u cdk'den kullanmaya başladık ve A11yModule kullanan bu yöntem kusursuz çalışıyor.


Merhaba ... cdkTrapFocusAutoCaptureÖzniteliği denemiyorum , ama ilk örneğinizi koduma dönüştürdüm. Bilinmeyen nedenlerle (benim için) AfterContentChecked yaşam döngüsü kancasıyla değil, yalnızca OnInit ile çalıştı. AfterContentChecked ile particolar'da, aynı formdaki yönerge olmadan odağı değiştirip (fare veya klavye ile) başka bir girişe taşıyamıyorum.
timhecker

@timhecker evet, bileşenlerimizi cdk yer paylaşımına geçirirken benzer bir sorunla karşılaştık (konumlandırma için bir kaplama yerine sadece css kullanıldığında işe yaradı). Yönergeyi kullanan bir bileşen görünür olduğunda girdiye süresiz olarak odaklandı. Bulduğum tek çözüm, güncellemede belirtilen cdk yönergelerini kullanmaktı.
SNDVLL

6

Yürütmeyi boole değiştikten sonra yapmak ve zaman aşımı kullanımını önlemek için şunları yapabilirsiniz:

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

constructor(private cd: ChangeDetectorRef) {}

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

5
Bunu açısal 7 ile denedim, işe yaramadı, zaman aşımı kullanımı iyi çalıştı
rekiem87

benim için açısal 8'de de çalışmıyor, kötü için setTimeout'a geri dönmeliyiz
Andrei Chivu

6

Angular'da, HTML'nin kendi içinde, odağı bir düğmeye tıklandığında girişe ayarlayabilirsiniz.

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

<input #myInput></input>

Bu yanıt, HTML'de başka bir öğeyi programlı olarak seçmenin basit bir yolunu gösteriyor, aradığım şey buydu (tüm "ilk odak" yanıtları, odak değiştirerek bir olaya nasıl tepki verileceğini çözmüyor) - ne yazık ki tepki vermem gerekiyordu mat-select selectionChangeddiğer kullanıcı arayüzü şeylerinden önce meydana gelen bir olaya, bu nedenle odağı o noktada çekmek işe yaramadı. Bunun yerine bir yöntem yazmak zorunda setFocus(el:HTMLElement):void { setTimeout(()=>el.focus(),0); }ve olay işleyicisinden diyoruz: <mat-select (selectionChanged)="setFocus(myInput)">. O kadar hoş değil, ama basit ve iyi çalışıyor. Teşekkürler!
Guss


1

Yalnızca Açısal Şablon kullanarak

<input type="text" #searchText>

<span (click)="searchText.focus()">clear</span>

"Malzeme kullanıyorsanız ..."
Andrew Koper

0

html bileşeni:

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

bileşen kontrolörü:

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

..ve ithalat unutma A11yModule gelen @ açısal / cdk / a11y

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

-1

Daha kolay yol da bunu yapmaktır.

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

3
Gerçekten doğrudan domu sorgulamak istemezsiniz.
Richard.Davenport
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.