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

如何仅在视图中保留货币格式而不修改模型值(货币指令)

解决方法:视图显示货币格式,模型保留数字类型

核心思路是分离视图显示值与模型值:仅在输入框中展示格式化后的货币字符串,而模型中始终存储原始数字。这样既满足显示需求,又不影响依赖数字类型的校验逻辑。

修改后的指令实现

import { Directive, ElementRef, Self, AfterViewInit, OnDestroy } from '@angular/core';
import { NgControl } from '@angular/forms';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

@Directive({
    selector: '[currencyFormatter]'
})
export class CurrencyFormatterDirective implements AfterViewInit, OnDestroy {
    constructor(public el: ElementRef, @Self() private ngControl: NgControl) {
        this.inputElement = el.nativeElement;
        this.formatter = new Intl.NumberFormat('en-IN', {
            style: 'currency',
            currency: 'INR',
            minimumFractionDigits: 0,
            maximumFractionDigits: 0
        });
    }

    private formatter: Intl.NumberFormat;
    private inputElement: HTMLInputElement;
    private destroy$ = new Subject<boolean>();

    ngAfterViewInit(): void {
        // 初始化时格式化模型初始值
        this.formatDisplayValue(this.ngControl.value);
        
        // 监听用户输入事件,处理输入内容
        this.inputElement.addEventListener('input', this.handleInput.bind(this));
        
        // 监听模型值变化,同步更新视图显示
        this.ngControl.control?.valueChanges
            .pipe(takeUntil(this.destroy$))
            .subscribe((value) => {
                if (value !== this.unformatDisplayValue()) {
                    this.formatDisplayValue(value);
                }
            });
    }

    private handleInput(): void {
        // 解析输入框内容为数字
        const rawInput = this.inputElement.value;
        const numericValue = this.unformatPrice(rawInput);
        
        // 更新模型值(确保为数字类型)
        if (!isNaN(numericValue)) {
            this.ngControl.viewToModelUpdate(numericValue);
        }
        
        // 重新格式化输入框显示内容
        this.formatDisplayValue(numericValue);
    }

    private formatDisplayValue(value: number | null | undefined): void {
        if (value == null || isNaN(value)) {
            this.inputElement.value = '';
            return;
        }
        this.inputElement.value = this.formatter.format(value);
    }

    private unformatDisplayValue(): number | null {
        return this.unformatPrice(this.inputElement.value);
    }

    private unformatPrice(value: string): number | null {
        if (!value) return null;
        // 移除货币符号、逗号等非数字字符
        const cleanedValue = value.replace(/[₹$, ]/g, '');
        return isNaN(Number(cleanedValue)) ? null : Number(cleanedValue);
    }

    ngOnDestroy(): void {
        this.destroy$.next(true);
        this.destroy$.complete();
        this.inputElement.removeEventListener('input', this.handleInput.bind(this));
    }
}

关键修改点说明

  • 分离视图与模型更新:直接监听输入框的input事件处理用户输入,仅更新输入框的显示值,通过viewToModelUpdate将解析后的数字同步到模型,确保模型始终存储数字类型。
  • 避免循环更新:模型值变化时,仅当模型值与输入框解析后的数字不一致时,才重新格式化显示,防止循环触发更新。
  • 健壮的解析逻辑:增强unformatPrice函数,处理空值、无效字符等场景,避免产生非法数值。
  • 兼容外部模型修改:监听模型值变化,确保表单重置、编程式修改模型值时,视图显示能同步更新。

输入元素无需修改

原输入元素的[min]和[max]校验逻辑可继续正常使用,因为模型值始终为数字类型,不受格式化显示的影响。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 11:25:18