Angular千分位格式化指令Ctrl+Z撤销失效问题求助
Angular千分位分隔指令Ctrl+Z撤销失效问题解决
开发的Angular千分位分隔指令可将数值(如1234)格式化为1,234显示,同时保持表单绑定值为原始数值,但存在Ctrl+Z撤销失效问题:输入1234并触发格式化后,按Ctrl+Z无法回退到空白状态。调试发现,撤销操作确实会尝试清空输入框,但指令的异步格式化逻辑会覆盖该操作,导致页面无变化。
问题根源
输入数值触发格式化时,指令通过setTimeout异步更新输入框显示值。若用户在该异步操作完成前执行Ctrl+Z,浏览器先清空输入框,但随后setTimeout的代码会将输入框重新设置为格式化后的1,234,覆盖撤销结果。此外,指令未区分普通输入和撤销/重做操作,导致这类浏览器原生操作被干扰。
修复方案
- 识别撤销/重做操作:通过
InputEvent.inputType判断是否为撤销(historyUndo)或重做(historyRedo)操作,这类操作下跳过格式化赋值,保留浏览器原生行为。 - 优化状态同步:确保输入框值为空时,正确同步表单值并避免不必要的格式化。
修改后的指令代码
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
相关产品推荐
相关产品推荐

