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

Angular自定义FormControl配置updateOn blur功能失效如何解决?

问题根因

你当前的自定义表单控件实现和Angular updateOn: 'blur' 的触发逻辑不匹配,核心问题有3个:

  • 输入时(keyup事件)就直接调用了markAsTouched()和onChange(),提前触发了表单校验、值更新,和updateOn: 'blur'要求的「失焦时才同步值、触发校验」逻辑冲突
  • writeValue方法仅在传入值非空时才赋值,初始值为空字符串时不会更新组件内部value,导致表单初始值同步错误
  • 没有在失焦事件中统一处理值同步、触摸状态标记逻辑,和Angular表单的更新时机不匹配
具体修改方案

1. 调整CustomTextareaComponent的TS代码

export class CustomTextareaComponent implements ControlValueAccessor {

  @Input() placeholder = '' // give a transloco string directly
  @Input() limit = 500
  @Input() error: boolean = false

  value: string | null = null
  currentNumberOfCharacters: number = 0
  isActive: boolean | undefined

  onChange: any = () => { }
  onTouch: any = () => { }
  touched = false
  disabled = false
  
  // 输入时仅更新组件内部状态,不通知外层表单
  changes(event: Event) {
    if (this.disabled) return
    this.value = event?.target ? (event?.target as HTMLTextAreaElement).value : ''
    this.currentNumberOfCharacters = this.value.length
  }

  // 新增失焦处理方法,仅在失焦时同步状态给外层表单
  handleBlur() {
    this.isActive = false
    if (this.disabled) return
    this.markAsTouched()
    this.onChange(this.value)
  }

  registerOnChange(providedFunction: any) {
    this.onChange = providedFunction
  }

  registerOnTouched(providedFunction: any) {
    this.onTouch = providedFunction
  }

  // 修正空值处理逻辑,空字符串也需要赋值
  writeValue(providedValue: any) {
    this.value = providedValue ?? ''
    this.currentNumberOfCharacters = this.value.length
  }

  setDisabledState(providedDisabledVal: any) {
    this.disabled = providedDisabledVal
  }

  markAsTouched() {
    if (!this.touched) {
      this.onTouch()
      this.touched = true
    }
  }

}

2. 调整CustomTextareaComponent的HTML代码

修改textarea的失焦事件绑定,调用新增的handleBlur方法:

<div class="plnd-textarea">
    <div class="plnd-textarea__field-container" [class.plnd-textarea__field-container--written]="value" [class.plnd-textarea__field-container--active]="isActive" [class.plnd-textarea__field-container--error]="error">
        <textarea 
            [placeholder]="placeholder | transloco" 
            [value]="value" 
            (keyup)="changes($event)" 
            (focus)="isActive=true" 
            (focusout)="handleBlur()" 
            [maxLength]="limit" 
            class="plnd-textarea__field"
        ></textarea>
    </div>
    <p class="plnd-textarea__characters">{{ currentNumberOfCharacters }}/{{ limit }} {{ 'common.characters' | transloco }}</p>
</div>

3. 同步修改custom-input组件

如果你的custom-input自定义控件逻辑和textarea一致,按照上面的逻辑同步修改即可:输入时仅更新内部状态,失焦时再同步值给外层表单、标记已触摸,修正writeValue的空值处理逻辑。

效果验证

修改完成后:

  • 输入字符时仅更新组件内部的输入显示和字符计数,不会提前触发表单校验,失焦后才会同步值、触发校验,不会出现输入时就报必填错误的问题
  • 表单提交时能正确拿到所有自定义控件的最新值,不会出现空值问题
  • 同时兼容updateOn: 'change'、updateOn: 'submit'的原有配置,不影响之前的正常逻辑

内容的提问来源于stack exchange,提问作者PepeW

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 03:36:03