首页 > 解决方案 > 实时计算总和 - 自定义货币格式(逗号作为小数分隔符)

问题描述

我想输入货币。

要求:

所以输入文本框中显示的值是100,55. 模型中的值为:100.55。我有这个,似乎工作正常。我还想显示当前的总和。目前,当我离开输入时,总和的值会更新。

例如:

代码: https ://stackblitz.com/edit/angular-1qzpaz?embed=1&file=src/app/app.component.html

我不想使用ngx-currency掩码,因为ng-currency掩码中的值已更改。例如用户类型:54 --> 将更改为:0,54。应该是54。

import { Component, OnInit } from '@angular/core';

import { User, UserService } from './user.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  providers: [UserService]
})
export class AppComponent implements OnInit {
  title = 'Users list';
  users: User[];

  constructor(private userService: UserService) { }

  ngOnInit(): void {
    this.userService.getUsers().then(users => this.users = users);
  }

  getSum(users: any[]): number{
    let sum: number = 0.00;
    users.forEach(u=>{
      console.log(u.money);
      if(u.money){
        let tempMoney = Number(u.money.toString().replace(',', '.'));
        sum += tempMoney;
      }
    });
    return sum;
  }

}
<h1>
  {{title}}
</h1>

<li *ngFor="let user of users">
<span >{{user.id}}</span> {{user.name}} 
  <input type="text" appCurrencyMask [fractionSize]="2"
                                     name="money"
                                     [(ngModel)]="user.money"/>
                                     Model: {{user.money }}
</li>
<br/>Total: {{getSum(users) | number : '1.2-2'}}
import {AfterViewInit, Directive, ElementRef, forwardRef, HostListener, Input, Renderer2} from '@angular/core';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
import {CurrencyMaskService} from "./currency-mask.service";

const noop = () => {
};

export const CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR: any = {
  provide: NG_VALUE_ACCESSOR,
  useExisting: forwardRef(() => CurrencyMaskDirective),
  multi: true
};

/*
* Custom Directive for Currency Mask
* The main requirements that drove the creation of this custom directive currency mask are:
* 1. Currency Mask had to be easy to implement across application. Directive control was used to accomplish that and everything is under one Module that can be easily imported.
* 2. Formatted value should be composed of: US dollar currency symbol '$' + value + 2 decimal point precision.
* 3. When user focus on the input, it should remove all formatting and only keep the decimal amount with the precision. If the input is blank and:
*   a. The user types "100" then unfocus, it should display $100.00
*   b. The user types "100.10" then unfocus, it should display $100.10
*   c. The user types ".25" then unfocus, it should display $0.25
* 4. User shouldn't be able to type anything that isn't numbers or decimal separator "."
* 5. Optional parameter for allowing negative numbers added. On Edit mode the the indicative of negative number is the minus "-" sign, but when
*     formatted we surround the value with parenthesis. So on input -300.12 will display as ($300.12).
*/
@Directive({
  selector: '[appCurrencyMask]',
  providers: [CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR]
})
export class CurrencyMaskDirective implements AfterViewInit, ControlValueAccessor {
  private el: HTMLInputElement;
  private innerValue: any;
  @Input('allowNegative')
  allowNegative: boolean;   // Optional Parameter to allow for negative number interaction

  @Input('fractionSize')
  fractionSize: number;

  constructor(private elementRef: ElementRef, private currencyMaskService: CurrencyMaskService, private renderer: Renderer2) {
    this.el = elementRef.nativeElement;
  }

  // Placeholders for the callbacks which are later providesd
  // by the Control Value Accessor
  private onTouchedCallback: () => void = noop;
  private onChangeCallback: (a: any) => void = noop;

  // set getter
  get value(): any {
    return this.innerValue;
  }

  // set accessor including call the onchange callback
  set value(v: any) {
    if (v !== this.innerValue) {
      this.innerValue = v;
      this.onChangeCallback(v);
    }
  }

  // From ControlValueAccessor interface
  writeValue(value: any) {
    if (value !== this.innerValue) {
      this.el.value = this.currencyMaskService.transform(value, this.allowNegative, this.fractionSize);
      if (value) {
        this.renderer.setAttribute(this.elementRef.nativeElement, 'value', value);
      }
      this.innerValue = value;
    }
  }

  // From ControlValueAccessor interface
  registerOnChange(fn: any) {
    this.onChangeCallback = fn;
  }

  // From ControlValueAccessor interface
  registerOnTouched(fn: any) {
    this.onTouchedCallback = fn;
  }

  ngAfterViewInit() {
    this.el.style.textAlign = 'right';
  }

  // On Focus remove all non-digit or decimal separator values
  @HostListener('focus', ['$event.target.value'])
  onfocus(value) {
    this.el.value = this.currencyMaskService.parse(value, this.allowNegative,  this.fractionSize);
  }

  // On Blue remove all symbols except last . and set to currency format
  @HostListener('blur', ['$event.target.value'])
  onBlur(value) {
    this.onTouchedCallback();
    this.el.value = this.currencyMaskService.transform(value, this.allowNegative,  this.fractionSize);
    this.innerValue = this.currencyMaskService.parse(this.el.value, this.allowNegative,  this.fractionSize);
    this.onChangeCallback(this.innerValue);
    if (this.innerValue) {
      this.renderer.setAttribute(this.elementRef.nativeElement, 'value', this.innerValue);
    }
  }

  // On Change remove all symbols except last . and set to currency format
  @HostListener('change', ['$event.target.value'])
  onChange(value) {
    this.el.value = this.currencyMaskService.transform(value, this.allowNegative,  this.fractionSize);
    this.innerValue = this.currencyMaskService.parse(this.el.value, this.allowNegative,  this.fractionSize);
    this.onChangeCallback(this.innerValue);
    if (this.innerValue) {
      this.renderer.setAttribute(this.elementRef.nativeElement, 'value', this.innerValue);
    }
  }

  // Prevent user to enter anything but digits and decimal separator
  @HostListener('keypress', ['$event'])
  onKeyPress(event) {
    //46 --> dot
    //44 --> comma
    const key = event.which || event.keyCode || 0;
    if (key === 45 && !this.allowNegative) {
      event.preventDefault();
    } else if (key === 45 && this.allowNegative) {
      // allow negative numbers
    } else if (key !== 44 && key > 31 && (key < 48 || key > 57)) {
      event.preventDefault();
    }
  }
}
import {Injectable} from '@angular/core';

const padding = "000000";

@Injectable()
export class CurrencyMaskService {
  private prefix: string;
  private thousandsSeparator: string;
  private decimalSeparator: string = ',';

  constructor() {
    this.prefix = '';
    this.thousandsSeparator = '.';
    this.decimalSeparator = ','
  }

  transform(value: string, allowNegative = false,  fractionSize: number) {
    if (value == undefined || value === '') {
      return null;
    }
    if (allowNegative) {
      value = value.toString();
      if (value.startsWith('(') || value.startsWith('-')) {
        value = '-' + value.substr(1, value.length).replace(/\(|\)|\$|\-/g, '');
      } else {
        value = value.replace(/\(|\)|\$|\-/g, '');
      }
    }
    value = value.toString().replace('.', ',');
    let [integer, fraction = ''] = (value || '').toString().split(this.decimalSeparator);
    fraction = fractionSize > 0
      ? this.decimalSeparator + (fraction + padding).substring(0, fractionSize) : "";
    // If user types .xx we can display 0.xx
    if (integer === '') {
      integer = '0.00';
    } else if (integer.startsWith('$')) {
      // If there are multiple transforms, remove the previous dollar sign (blur and change at the same time)
      integer = integer.substr(1, integer.length);
    } else if (allowNegative && integer.startsWith('-')) {
      // If user inputs negative number set to paranthesis format
      integer = integer.substr(1, integer.length);
      return '(' + this.prefix + integer + fraction + ')';
    }
    return this.prefix + integer + fraction;
  }

  parse(value: string, allowNegative = false,  fractionSize: number) {
    let [integer, fraction = ''] = (value || '').split(this.decimalSeparator);
    integer = integer.replace(new RegExp(/[^\d\.]/, 'g'), '');
    fraction = parseInt(fraction, 10) > 0 && 2 > 0 ? this.decimalSeparator + (fraction + '000000').substring(0, 2) : '';
    if (allowNegative && value.startsWith('(') && value.endsWith(')')) {
      return (-1 * parseFloat(integer + fraction)).toString();
    } else {
      return (integer + fraction).replace(",", ".").toString();
    }
  }
}

标签: angular

解决方案


不,我认为问题在于主机听众的指示。


推荐阅读