如何监听Angular FormGroup内输入框的聚焦与失焦状态
Angular 监听FormGroup内所有输入框聚焦/失焦的最优方案
两种实现方案都不需要给每个输入框单独绑定事件,适配你移动端软键盘的使用场景:
方案1:自定义全局监听指令(复用性最高,推荐)
只需要给FormGroup所在的父容器挂载一次指令,即可自动监听所有内部输入框的状态。
- 生成指令:
ng generate directive form-focus-listener
- 指令逻辑:
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); } } } }
- 页面使用:
<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
相关产品推荐
相关产品推荐

