Angular 2+ Currency Mask

笑着哭i 提交于 2019-12-08 07:27:01

问题


I tried a couple of the currency masks out there for Angular 2+ but my requirements don't really fit them so I'm trying to create a Directive myself. Here's what I need:

  1. Allow for null values. Keep the value as a number, not a string.
  2. On Focus remove currency format. On blur re-format.
  3. Enter number from right to left (so for $250.00 the user would have to type [2] -> 5 -> 0. (so not starting from the decimal point)
  4. Don't allow users to type anything but numbers and decimal separator.
  5. Text-align right

I got most of this working.

The only issue is that it only works on focus/blur/keypress. Since the data is loaded async for my components, no event in the directive is triggered so the initial load of existing dollar amounts isn't formatted. Here's my Directive so far:

import { Directive, ElementRef, HostListener, Input, AfterViewInit } from '@angular/core';
import { CurrencyMaskService } from './currency-mask.service';

@Directive({
  selector: '[appCurrencyMask]'
})
export class CurrencyMaskDirective implements AfterViewInit {
  private el: HTMLInputElement;
  constructor(private elementRef: ElementRef, private currencyMaskService: CurrencyMaskService) {
    this.el = elementRef.nativeElement;
  }

  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);
  }

  // On Blur remove all symbols except last . and set to currency format
  @HostListener('blur', ['$event.target.value'])
  onBlur(value) {
    this.el.value = this.currencyMaskService.transform(value);
  }

  // On Blur remove all symbols except last . and set to currency format
  @HostListener('change', ['$event.target.value'])
  onChange(value) {
    this.el.value = this.currencyMaskService.transform(value);
  }

  // Prevent user to enter anything but digits and decimal separator
  @HostListener('keypress', ['$event'])
  onKeyPress(event) {
    const key = event.which || event.keyCode || 0;
    if (key !== 46 && key > 31 && (key < 48 || key > 57)) {
      event.preventDefault();
    }
  }
}

回答1:


The Reddit r/Angular2 community was able to help me find the answer: https://www.reddit.com/r/Angular2/comments/82oojo/angular_2_currency_mask/

I was completely missing on how to keep track of the value itself, by using the ControlValueAccessor I was able to accomplish what I wanted. See code below:

import { Directive, ElementRef, HostListener, AfterViewInit, Input, forwardRef } from '@angular/core';
import { CurrencyMaskService } from './currency-mask.service';
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';

const noop = () => {};

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

@Directive({
  selector: '[appCurrencyMask]',
  providers: [CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR]
})
export class CurrencyMaskDirective implements AfterViewInit, ControlValueAccessor {
  private el: HTMLInputElement;
  private innerValue: any = 0;
  constructor(private elementRef: ElementRef, private currencyMaskService: CurrencyMaskService) {
    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);
    }
  }

  // Set touched on blur
  // onBlur() {
  //   this.onTouchedCallback();
  // }

  // From ControlValueAccessor interface
  writeValue(value: any) {
    if (value !== this.innerValue) {
      this.el.value = this.currencyMaskService.transform(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);
  }

  // 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.onChangeCallback(this.currencyMaskService.parse(this.el.value));
  }

  // 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.onChangeCallback(this.currencyMaskService.parse(this.el.value));
  }

  // Prevent user to enter anything but digits and decimal separator
  @HostListener('keypress', ['$event'])
  onKeyPress(event) {
    const key = event.which || event.keyCode || 0;
    if (key !== 46 && key > 31 && (key < 48 || key > 57)) {
      event.preventDefault();
    }
  }
}

*EDIT: Here's the full code I ended up using: https://github.com/LeoTanoue/ngx-currency-mask



来源:https://stackoverflow.com/questions/49139595/angular-2-currency-mask

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!