将重点放在<input>元素上


107

我正在使用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();
  }

搜索框未设置为焦点。

我怎样才能做到这一点?

Answers:


128

像这样修改show搜索方法

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

15
为什么我们需要使用setTimeout?布尔值的变化不是同步的吗?
安德烈·罗苏

6
那不是和zonejs混在一起吗?
lawphotog

1
@AndreiRosu,如果没有它,我会因为未呈现更改而报错
Islam Q.

13
这是可行的,但是没有明确的解释就是魔术。魔术经常中断。
约翰·怀特

1
打开面板时,我试图将元素集中在面板中,因此0超时对我不起作用,但这确实做到了:setTimeout(() => { this.searchElement.nativeElement.focus() }, 100)
tclark333

41

您应该为此使用html自动对焦

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

注意:如果您的组件被保留并重新使用,则它将仅在第一次附着片段时自动聚焦。可以通过使用全局dom侦听器来克服此问题,该侦听器在连接dom片段时检查dom片段内的autofocus属性,然后重新应用它或通过JavaScript进行聚焦。


46
每个页面只能刷新一次,不能多次刷新。
moritzg

23

显示该指令后,它将立即聚焦并选择元素中的任何文本。在某些情况下,这可能需要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请求的情况下对我有用的唯一解决方案
Abdelhalim FELLAGUE CHEBRA

1
我发现此解决方案比接受的答案更可取。在代码重用的情况下它更干净,并且以Angular为中心。
derekbaker783

同样不要忘记在模块的声明中添加指令。
伊利亚·多林

11

我要权衡一下(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
这似乎与接受的答案
Liam

9

这在没有setTimeout的Angular 8中可以正常工作:

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起作用的原因相同,但是在Angular中运行setTimeout时,它将绕过Zone.js并再次运行所有检查,并且它起作用,因为在setTimeout完成时所有更改都已完成。使用正确的生命周期挂钩(AfterContentChecked),可以达到相同的结果,但优点是不会运行额外的周期。当检查并通过所有更改后,该函数将触发,并在AfterContentInit和DoCheck挂钩之后运行。如果我错了,请纠正我。

更多https://angular.io/guide/lifecycle-hooks上的生命周期和变更检测

更新:如果使用的是Angular Material CDK,即a11y-package,我发现了一种更好的方法。首先,在模块中导入A11yModule,以声明您在其中具有输入字段的组件。然后使用cdkTrapFocuscdkTrapFocusAutoCapture指令,并在html中使用此类,并在输入中设置tabIndex:

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

我们的下拉菜单在定位和响应速度方面遇到了一些问题,并开始使用cdk中的OverlayModule,而使用A11yModule的此方法可以正常工作。


嗨...我不尝试使用cdkTrapFocusAutoCapture属性,但是我将您的第一个示例更改为我的代码。由于未知的原因(对我而言),它不适用于AfterContentChecked生命周期挂钩,而仅适用于OnInit。特别是在AfterContentChecked中,如果没有相同格式的该指令,我将无法更改焦点并移动(使用鼠标或键盘)到另一个输入上。
timhecker

@timhecker是的,当我们将组件迁移到cdk覆盖图时,我们遇到了类似的问题(仅使用css而不是覆盖图进行定位时有效)。当使用该指令的组件可见时,它会无限期地专注于输入。我发现的唯一解决方案是使用更新中提到的cdk指令。
SNDVLL

7

在Angular中,您可以在HTML本身内将焦点设置为单击按钮时输入。

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

<input #myInput></input>

此答案显示了一种以编程方式选择HTML中另一个元素的简单方法,这就是我一直在寻找的(所有“初始焦点”答案都无法解决如何通过更改焦点来响应事件)-不幸的是,我需要做出反应mat-select selectionChanged发生在其他UI事物之前的事件,因此无法集中精力进行此操作。相反,我不得不编写一个方法setFocus(el:HTMLElement):void { setTimeout(()=>el.focus(),0); }并从事件处理程序中调用它:<mat-select (selectionChanged)="setFocus(myInput)">。不是很好,但是简单,效果很好。谢谢!
格斯

没为我工作。我收到错误TypeError:无法读取未定义的属性“焦点”
Reza Taba

@RezaTaba,您调用了函数“ myInput.focus()”还是刚刚编写了“ myInput.focus”?另外,请确保您的输入元素在渲染的div内(例如* ngIf false可能导致错误)
shaheer shukur

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
Andrei Chivu


1

仅使用角度模板

<input type="text" #searchText>

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

“如果您正在使用Material ...”
Andrew Koper

错误TypeError:无法读取未定义的属性“焦点”
Reza Taba

1

组件的html:

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

组件的控制者:

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

..和不要忘了进口A11yModule@角/ CDK / A11Y

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

最佳解决方案。谢谢。其他解决方案给了我错误。
Reza Taba

1

我有相同的情况,这对我有用,但是我没有您拥有的“隐藏/显示”功能。因此,也许您可​​以先检查在始终可见的字段时是否获得焦点,然后尝试解决更改可见性时为何不起作用的原因(可能这就是为什么需要施加睡眠或保证的原因)

要设置焦点,这是您需要做的唯一更改:

您的HTML垫输入应为:

<input #yourControlName matInput>

在您的TS类中,在变量部分(

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

您的按钮很好,调用:

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

就是这样。您可以在我发现的这篇文章中检查此解决方案,因此感谢-> https://codeburst.io/focusing-on-form-elements-the-angular-way-e9a78725c04f


-1

也更容易做到这一点。

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

3
您真的不想直接查询dom。
理查德。达文波特
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.