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

Angular中如何为ngModel绑定的数值添加两位小数显示?

问题描述

有一个绑定数值类型变量sipmaximum的输入框,当前显示示例为9999,需要将其格式化为9999.00的格式展示。相关代码如下:

模板代码

<tr>
   <th>SIP maximum </th>
   <td>
      <input id="sipmaximum" name="sipmaximum" type="text" class="form-control" [(ngModel)]="sipmaximum" style="width: 70%" required greaterThanZero />
   </td>
</tr>

JSON数据

{
   "AANBOD": {
      "ISINAANBOD": "Y",
      "RISK": "3",
      "COMMENTAIRE_FR": "",
      "COMMENTAIRE_NL": "",
      "QTTE_TOTALE": 0,
      "QTTE_FAITE": 0,
      "QTTE_REST": 0,
      "QTTE_MIN": 100,
      "QTTE_MAX": 15000,
      "MULTIPLE": 0,
      "DATEVALID_DEBUT": "2019-09-16",
      "DATEVALID_FIN": "2050-12-31",
      "MARKET": "",
      "SIPAGENCEFLAG": "Y",
      "SIPCLIENTFLAG": "N",
      "SIPMINIMUM": 50,
      "SIPMAXIMUM": 9999
   }
}

组件代码

export class EditPurchasableSecuritiesComponent implements OnInit, OnDestroy {
  private unsubscribe$ = new Subject<void>();

  svm: string;
  risk: string;
  datedeb: Date;
  datefin: Date;
  min: number;
  max: number;
  sipminimum: number;
  sipmaximum: number;

  TIT: Tit[] = [];

  constructor(
    private service: EditPurchasableSecuritiesService,
    private activatedRoute: ActivatedRoute,
    private location: Location,
    private router: Router
  ) {}

  ngOnInit(): void {
    let svm: string | null;
    svm = this.activatedRoute.snapshot.paramMap.get("svm");
    if (!svm) {
      this.goBack();
      return;
    }
    this.svm = svm;
    this.editPurchasableSecuritiesManagement(this.svm);
    this.datedeb = new Date();
  }

  ngOnDestroy(): void {
    this.unsubscribe$.next();
    this.unsubscribe$.complete();
  }

  editPurchasableSecuritiesManagement(svm: string): void {
    this.service
      .editPurchasableSecuritiesManagement(this.svm)
      .pipe(takeUntil(this.unsubscribe$))
      .subscribe((res) => {
        if (res.RETURNCODE === ApiResponseCodeEnum.Ok) {
          this.TIT = res.TIT;
          this.risk = res.TIT[0].AANBOD.RISK;
          this.datedeb = new Date();
          this.datefin = new Date(res.TIT[0].AANBOD.DATEVALID_FIN);
          this.min = res.TIT[0].AANBOD.QTTE_MIN;
          this.max = res.TIT[0].AANBOD.QTTE_MAX;
          this.sipminimum = res.TIT[0].AANBOD.SIPMINIMUM;
          this.sipmaximum = res.TIT[0].AANBOD.SIPMAXIMUM;
        }
      });
  }

  goBack(): void {
    this.location.back();
  }

  updatePurchasableSecuritiesManagement(): void {
    this.service
      .updatePurchasableSecuritiesManagement(
        this.svm,
        this.risk,
        this.datedeb,
        this.datefin,
        this.min,
        this.max,
        this.sipminimum,
        this.sipmaximum
      )
      .pipe(takeUntil(this.unsubscribe$))
      .subscribe((res) => {
        if (res.RETURNCODE === ApiResponseCodeEnum.Ok) {
          this.router
            .navigateByUrl("/", { skipLocationChange: true })
            .then(() => {
              this.router.navigate([
                "functional-management/list-purchasable-securities",
              ]);
            });
        }
      });
  }
}

接口定义

export interface PurchasableSecuritieResponse extends ApiResponse {
    TIT: Tit[];
  }
  
  export interface Tit {
    ADVTITRE: {
      BASIQUETITRE: {
        SVM: number;
        ISIN: string;
        STOCK: string;
        LABEL: string;
        PLACE: number;
        PLACELABEL: string;
        REGR: number;
        REGRLABEL: string;
        DEVISE: string;
        COURS: {
          DATE: string;
          TIME: string;
          VALUE: number;
          HIGH: number;
          LOW: number;
          CLOTURE: number;
        };
      };
      EXIST: number;
      SECE: number;
      SECE_LIB: string;
      PAYS: number;
      PAYS_LIB: string;
      YPMAR: string;
      CUM: string;
      DEVCP: string;
      CPPX: number;
      DATCPN: string;
      COURG: number;
      DCOURG: string;
      COURB: number;
      DCOURB: string;
      CODEF: number;
      ECHEANCE: string;
      RADIATION: string;
      COTATION: string;
      RATING: string;
      COURUS: number;
      BRUT: number;
      ECH1: string;
      ECH2: string;
      ECH3: string;
      ECH4: string;
      ECH5: string;
      TAUX1: number;
      TAUX2: number;
      TAUX3: number;
      TAUX4: number;
      TAUX5: number;
      HYPERLINK: string;
      INFOBOU: number;
      HISTCOURS: number;
      GROUPE: string;
      COUPON: number;
      WARRANT: number;
      OPTION: number;
      REVERSE: number;
      RECO: number;
      SIGNAUX: number;
    };
    TITARG: {
      COMPLEXITY: string;
      CREATTIME: string;
      CREATUSER: string;
      MODTIME: string;
      MODUSER: string;
      CREATMODE: string;
    };
    AANBOD: {
      ISINAANBOD: string;
      RISK: string;
      COMMENTAIRE_FR: string;
      COMMENTAIRE_NL: string;
      QTTE_TOTALE: number;
      QTTE_FAITE: number;
      QTTE_REST: number;
      QTTE_MIN: number;
      QTTE_MAX: number;
      MULTIPLE: number;
      DATEVALID_DEBUT: Date;
      DATEVALID_FIN: Date;
      MARKET: string;
      SIPAGENCEFLAG: string;
      SIPCLIENTFLAG: string;
      SIPMINIMUM: number;
      SIPMAXIMUM: number;
      PRIX_MARCHE: number;
      PRIX_CLIENT: number;
    };
    ...
解决方案

因为输入框是双向绑定的数值类型,要实现显示为9999.00格式,同时保证用户输入后能正确转换回数值类型,可采用以下几种方式:

方法一:使用DecimalPipe拆分双向绑定

利用Angular内置的DecimalPipe格式化显示,拆分双向绑定为单向绑定和输入事件,分别处理显示与值更新:

  1. 组件中注入DecimalPipe:
import { DecimalPipe } from '@angular/common';

// ...

constructor(
  // 其他依赖
  private decimalPipe: DecimalPipe
) {}
  1. 修改模板,拆分[(ngModel)]:
<tr>
   <th>SIP maximum </th>
   <td>
      <input 
        id="sipmaximum" 
        name="sipmaximum" 
        type="text" 
        class="form-control" 
        [ngModel]="decimalPipe.transform(sipmaximum, '1.2-2')" 
        (ngModelChange)="onSipMaximumChange($event)"
        style="width: 70%" 
        required 
        greaterThanZero 
      />
   </td>
</tr>

'1.2-2'表示最少1位整数、固定2位小数。

  1. 组件中添加输入转换方法:
onSipMaximumChange(value: string): void {
  // 过滤非数字和小数点字符,转换为数值
  const numericValue = parseFloat(value.replace(/[^0-9.]/g, '')) || 0;
  this.sipmaximum = numericValue;
}

方法二:自定义双向绑定指令(复用场景)

如果需要在多个组件中复用该格式化逻辑,可自定义指令:

  1. 创建NumberFormatDirective指令:
import { Directive, ElementRef, HostListener, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { DecimalPipe } from '@angular/common';

@Directive({
  selector: '[appNumberFormat]',
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => NumberFormatDirective),
      multi: true
    }
  ]
})
export class NumberFormatDirective implements ControlValueAccessor {
  private onChange: (value: number) => void;
  private onTouched: () => void;

  constructor(
    private el: ElementRef,
    private decimalPipe: DecimalPipe
  ) {}

  writeValue(value: number): void {
    // 格式化数值并显示
    const formattedValue = this.decimalPipe.transform(value, '1.2-2');
    this.el.nativeElement.value = formattedValue || '';
  }

  registerOnChange(fn: (value: number) => void): void {
    this.onChange = fn;
  }

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

  @HostListener('input', ['$event.target.value'])
  onInput(value: string): void {
    // 转换输入为数值
    const numericValue = parseFloat(value.replace(/[^0-9.]/g, '')) || 0;
    this.onChange(numericValue);
    // 重新格式化确保显示格式正确
    this.el.nativeElement.value = this.decimalPipe.transform(numericValue, '1.2-2');
  }

  @HostListener('blur')
  onBlur(): void {
    this.onTouched();
  }
}
  1. 在模块中声明指令后,模板中直接使用:
<input 
  id="sipmaximum" 
  name="sipmaximum" 
  type="text" 
  class="form-control" 
  [(ngModel)]="sipmaximum" 
  appNumberFormat
  style="width: 70%" 
  required 
  greaterThanZero 
/>

方法三:组件内直接处理(简单场景)

仅在当前组件使用时,可直接在组件内拆分显示与存储变量:

  1. 新增显示用字符串变量:
sipmaximum: number;
sipmaximumDisplay: string; // 存储格式化后的显示值
  1. 获取接口数据时格式化赋值:
editPurchasableSecuritiesManagement(svm: string): void {
  this.service
    .editPurchasableSecuritiesManagement(this.svm)
    .pipe(takeUntil(this.unsubscribe$))
    .subscribe((res) => {
      if (res.RETURNCODE === ApiResponseCodeEnum.Ok) {
        // ...其他变量赋值
        this.sipmaximum = res.TIT[0].AANBOD.SIPMAXIMUM;
        // 格式化为两位小数的字符串
        this.sipmaximumDisplay = this.sipmaximum.toFixed(2);
      }
    });
}
  1. 修改模板绑定显示变量,添加失焦转换逻辑:
<input 
  id="sipmaximum" 
  name="sipmaximum" 
  type="text" 
  class="form-control" 
  [(ngModel)]="sipmaximumDisplay" 
  (blur)="onSipMaximumBlur()"
  style="width: 70%" 
  required 
  greaterThanZero 
/>
  1. 组件中添加失焦转换方法:
onSipMaximumBlur(): void {
  const numericValue = parseFloat(this.sipmaximumDisplay.replace(/[^0-9.]/g, '')) || 0;
  this.sipmaximum = numericValue;
  // 重新格式化确保显示正确
  this.sipmaximumDisplay = numericValue.toFixed(2);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 01:02:32