问题
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:
- Allow for null values. Keep the value as a number, not a string.
- On Focus remove currency format. On blur re-format.
- 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)
- Don't allow users to type anything but numbers and decimal separator.
- 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