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

Angular千分位格式化指令Ctrl+Z撤销失效问题求助

Angular千分位分隔指令Ctrl+Z撤销失效问题解决

开发的Angular千分位分隔指令可将数值(如1234)格式化为1,234显示,同时保持表单绑定值为原始数值,但存在Ctrl+Z撤销失效问题:输入1234并触发格式化后,按Ctrl+Z无法回退到空白状态。调试发现,撤销操作确实会尝试清空输入框,但指令的异步格式化逻辑会覆盖该操作,导致页面无变化。

问题根源

输入数值触发格式化时,指令通过setTimeout异步更新输入框显示值。若用户在该异步操作完成前执行Ctrl+Z,浏览器先清空输入框,但随后setTimeout的代码会将输入框重新设置为格式化后的1,234,覆盖撤销结果。此外,指令未区分普通输入和撤销/重做操作,导致这类浏览器原生操作被干扰。

修复方案

  1. 识别撤销/重做操作:通过InputEvent.inputType判断是否为撤销(historyUndo)或重做(historyRedo)操作,这类操作下跳过格式化赋值,保留浏览器原生行为。
  2. 优化状态同步:确保输入框值为空时,正确同步表单值并避免不必要的格式化。

修改后的指令代码

import { Directive, ElementRef, HostListener, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

@Directive({
  selector: '[thousandSeperator]',
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => ThousandSeperatorDirective),
      multi: true,
    },
  ],
  standalone: true,
})
export class ThousandSeperatorDirective implements ControlValueAccessor {
  private onChange: (_: any) => void = () => {};
  private onTouched: () => void = () => {};
  constructor(private el: ElementRef) {}

  @HostListener('input', ['$event'])
  @HostListener('paste', ['$event'])
  @HostListener('blur', ['$event'])
  onInput(event: Event | ClipboardEvent) {
    const inputEl = event.target as HTMLInputElement;
    const rawValue = inputEl.value;
    const unformattedValue = rawValue.replace(/,/g, '');

    this.onChange(unformattedValue);

    // 跳过撤销/重做操作,保留浏览器原生行为
    if (event instanceof InputEvent && ['historyUndo', 'historyRedo'].includes(event.inputType)) {
      return;
    }

    if (rawValue) {
      const formattedValue = this.formatValue(unformattedValue);
      // 只有当前输入框值与格式化值不一致时才更新,避免覆盖用户操作
      if (inputEl.value !== formattedValue) {
        setTimeout(() => (inputEl.value = formattedValue), 0);
      }
    } else {
      // 确保输入框为空时同步显示
      inputEl.value = '';
    }
  }

  writeValue(value: any): void {
    if (value || value === 0) {
      this.el.nativeElement.value = this.formatValue(value.toString());
    } else {
      this.el.nativeElement.value = '';
    }
  }

  registerOnChange(fn: any): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }

  private formatValue(value: string): string {
    const [integer, decimal] = value.split('.');
    const hasDecimalPoint = value.includes('.');
    const formattedInteger = integer.replace(/,/g, '').replace(/\B(?=(\d{3})+(?!\d))/g, ',');
    
    if (hasDecimalPoint) {
      return decimal ? `${formattedInteger}.${decimal}` : `${formattedInteger}.`;
    }
    return formattedInteger;
  }
}

关键修改点

  • 新增操作类型判断:检测inputType为historyUndo或historyRedo时,直接返回,不执行格式化赋值,让浏览器原生撤销/重做生效。
  • 添加值对比判断:更新输入框值前,先对比当前值与格式化值,避免不必要的更新。
  • 空值处理优化:明确处理输入框为空的情况,确保显示与表单值同步。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 13:05:53