Angular 5集合关注输入元素

Bob*_*Bob 50 angular angular-forms angular5

我正在使用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();
  }
Run Code Online (Sandbox Code Playgroud)

搜索框未设置为焦点.我怎样才能做到这一点?谢谢.

小智 59

像这样修改show search方法

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

  • 这可行,但如果没有明确的解释,它就是神奇的。魔法经常破裂。 (14认同)
  • 为什么我们需要使用setTimeout?布尔值的变化不是同步的吗? (5认同)
  • setTimeout 会刷新所有布局,即使是在“onPush”中。最好触发 `this.cdr.detectChanges()` 而不是 `setTImeout`。 (5认同)
  • tl;dr:使用 setTimeout 使您的代码异步,方法是将函数执行添加到事件循环,并在执行时第二次触发更改检测。它确实对性能有影响。 (3认同)
  • 那不是和zonejs混在一起吗? (2认同)
  • 当面板打开时,我试图将我的元素集中在面板内,因此 0 超时对我不起作用,但这确实有效: `setTimeout(() =&gt; { this.searchElement.nativeElement.focus() }, 100)` (2认同)
  • @John White 与 React 和 Vue 一样,Angular 根据计算间隔批量处理 DOM 渲染更改。这提高了性能(如果没有批处理,页面将在每行上重新加载)。现在魔术的一个原因是,发生的事情是由于批处理,ngIf 语句尚未在 DOM 中渲染,使得元素 ref DOM 丢失。现在的解决方案是,添加超时会在计时器结束时创建一个新批次(即使它为 0)。现在,由于 ngIf 语句已更新,DOM 元素不再丢失。 (2认同)

N-a*_*ate 24

您应该使用html 自动对焦:

<input *ngIf="show" #search type="text" autofocus /> 
Run Code Online (Sandbox Code Playgroud)

注意:如果您的组件被持久化并重用,它将仅在第一次创建片段时自动聚焦.这可以通过拥有一个全局dom监听器来解决,该监听器在连接时检查dom片段内的自动聚焦属性,然后重新应用它或通过javascript聚焦.

  • 每个页面只能刷新一次,不能多次刷新。 (18认同)

SND*_*VLL 13

这在没有 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();
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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

更新:如果使用 Angular Material CDK,a11y 包,我发现了一种更好的方法来做到这一点。首先在模块中导入A11yModule,声明您具有输入字段的组件。然后使用cdkTrapFocuscdkTrapFocusAutoCapture指令并在 html 中像这样使用并在输入上设置 tabIndex:

<div class="dropdown" cdkTrapFocus cdkTrapFocusAutoCapture>
    <input type="text tabIndex="0">
</div>
Run Code Online (Sandbox Code Playgroud)

我们的下拉菜单在定位和响应方面存在一些问题,因此开始使用 cdk 中的 OverlayModule,而这种使用 A11yModule 的方法可以完美运行。


sha*_*kur 10

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

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

<input #myInput></input>
Run Code Online (Sandbox Code Playgroud)


小智 9

要在布尔值更改后执行并避免使用超时,您可以执行以下操作:

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

constructor(private cd: ChangeDetectorRef) {}

showSearch(){
  this.show = !this.show;  
  this.cd.detectChanges();
  this.searchElement.nativeElement.focus();
}
Run Code Online (Sandbox Code Playgroud)

  • 我用angular 7试过这个,它没有用,使用超时工作正常 (5认同)

ggr*_*num 8

显示该指令后,它将立即聚焦并选择元素中的任何文本。在某些情况下,这可能需要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();
  }
}
Run Code Online (Sandbox Code Playgroud)

在HTML中:

 <mat-form-field>
     <input matInput type="text" appPrefixFocusAndSelect [value]="'etc'">
 </mat-form-field>
Run Code Online (Sandbox Code Playgroud)

  • 我发现这个解决方案比接受的答案更可取。它在代码重用的情况下更干净,并且更以 Angular 为中心。 (3认同)

Ric*_*ino 8

我要权衡一下(Angular 7解决方案)

输入[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);
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 这似乎与[接受的答案](/sf/answers/3501013281/)几乎相同 (2认同)

Yog*_*rtu 8

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

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

你的 Html mat 输入应该是:

<input #yourControlName matInput>
Run Code Online (Sandbox Code Playgroud)

在您的 TS 类中,在变量部分中像这样引用(

export class blabla...
    @ViewChild("yourControlName") yourControl : ElementRef;
Run Code Online (Sandbox Code Playgroud)

你的按钮没问题,正在调用:

  showSearch(){
       ///blabla... then finally:
       this.yourControl.nativeElement.focus();
}
Run Code Online (Sandbox Code Playgroud)

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


Tim*_*mon 6

还有一个名为 DOM 属性cdkFocusInitial,它适用于我的输入。您可以在这里阅读更多相关信息: https: //material.angular.io/cdk/a11y/overview


Cic*_*chy 6

组件的html:

<input [cdkTrapFocusAutoCapture]="show" [cdkTrapFocus]="show">
Run Code Online (Sandbox Code Playgroud)

组件控制器:

showSearch() {
  this.show = !this.show;    
}
Run Code Online (Sandbox Code Playgroud)

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

import { A11yModule } from '@angular/cdk/a11y'
Run Code Online (Sandbox Code Playgroud)

  • 是的,这就是2022年的做法! (5认同)
  • 很好的解决方案 - 效果很好并且无痛。 (3认同)
  • 最佳解决方案。谢谢。其他解决方案给了我错误。 (2认同)