You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何监听Angular FormGroup内输入框的聚焦与失焦状态

Angular 监听FormGroup内所有输入框聚焦/失焦的最优方案

两种实现方案都不需要给每个输入框单独绑定事件,适配你移动端软键盘的使用场景:


方案1:自定义全局监听指令(复用性最高,推荐)

只需要给FormGroup所在的父容器挂载一次指令,即可自动监听所有内部输入框的状态。

  1. 生成指令:
ng generate directive form-focus-listener
  1. 指令逻辑:
import { Directive, Output, EventEmitter, HostListener } from '@angular/core';

@Directive({
  selector: '[formFocusListener]',
  standalone: true // 非独立组件去掉这行,声明到对应模块的declarations里即可
})
export class FormFocusListenerDirective {
  // 对外输出聚焦状态变化
  @Output() focusStateChange = new EventEmitter<boolean>();
  // 计数器避免切换输入框时状态反复跳变
  private focusCount = 0;

  // 监听所有子元素focus事件,开启捕获模式解决focus不冒泡的问题
  @HostListener('focus', ['$event.target'], true)
  onFocus(target: HTMLElement) {
    // 仅处理输入类元素,可根据业务自定义支持的标签
    if (['INPUT', 'TEXTAREA'].includes(target.tagName)) {
      this.focusCount++;
      this.focusStateChange.emit(true);
    }
  }

  // 监听所有子元素blur事件
  @HostListener('blur', ['$event.target'], true)
  onBlur(target: HTMLElement) {
    if (['INPUT', 'TEXTAREA'].includes(target.tagName)) {
      this.focusCount--;
      // 所有输入框都失焦才更新为false,切换输入框时不会触发状态变更
      if (this.focusCount <= 0) {
        this.focusCount = 0;
        this.focusStateChange.emit(false);
      }
    }
  }
}
  1. 页面使用:
<form [formGroup]="yourForm" formFocusListener (focusStateChange)="focused = $event">
  <!-- 内部所有输入框不需要加任何额外绑定 -->
  <input formControlName="userName" type="text">
  <input formControlName="phone" type="tel">
  <textarea formControlName="remark"></textarea>
</form>

方案2:组件内直接监听(轻量,适合单表单场景)

如果只有一个页面需要该能力,不需要全局复用,可以直接在当前组件内加监听逻辑,不用额外定义指令:

import { Component, HostListener } from '@angular/core';
import { FormGroup, FormBuilder } from '@angular/forms';

@Component({
  selector: 'app-form-page',
  templateUrl: './form-page.component.html'
})
export class FormPageComponent {
  yourForm: FormGroup;
  focused = false;
  private focusCount = 0;

  constructor(private fb: FormBuilder) {
    this.yourForm = this.fb.group({
      // 你的表单控件定义
    })
  }

  @HostListener('focus', ['$event.target'], true)
  onFocus(target: HTMLElement) {
    const controlName = target.closest('[formControlName]')?.getAttribute('formControlName');
    // 仅监听当前FormGroup内的输入框
    if (['INPUT', 'TEXTAREA'].includes(target.tagName) && controlName && this.yourForm.contains(controlName)) {
      this.focusCount++;
      this.focused = true;
    }
  }

  @HostListener('blur', ['$event.target'], true)
  onBlur(target: HTMLElement) {
    const controlName = target.closest('[formControlName]')?.getAttribute('formControlName');
    if (['INPUT', 'TEXTAREA'].includes(target.tagName) && controlName && this.yourForm.contains(controlName)) {
      this.focusCount--;
      if (this.focusCount <= 0) {
        this.focusCount = 0;
        this.focused = false;
      }
    }
  }
}

注意事项

  • 如果使用自定义输入组件,把自定义组件的标签名添加到判断数组中即可,比如['INPUT', 'TEXTAREA', 'APP-CUSTOM-INPUT']
  • 计数器逻辑可以避免用户切换输入框时,出现状态反复跳变导致的页面内容闪烁,刚好适配移动端软键盘不会频繁收起弹出的需求。

内容的提问来源于stack exchange,提问作者Raphaël Balet

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 20:09:02