问题
I am trying to format a number in a text input in the locale of the user. I tried to use DecimalPipe
and a custom self-made pipe and both work at first. The problem is, as soon as the number is formated into a string, the pipe doesn't work anymore since it is supposed to be applied on a number.
So for example when I type 1234.23
, it displays 1 234,23
but the DecimalPipe doesn't work on 1 234,23
and I get the NaN error.
I didn't find anything that allows me to turn back the formatted text into a number again to reapply my pipe. For instance Number("1 234,23")
doesn't work and returns NaN...
It is easy to get rid of the thousand separator space, but sometimes it is a comma and sometimes the comma is the decimal separator, so I need the locale to define how the string is formatted into a number.
回答1:
I have created custom input component for this purpose and it is working fine in many system without any issue.
I have used ngx-translate for handling Internationalization in my system. Here is my implementation.
HTML
<input #input type="text" appOnlyNumbers (input)="onChange($event)" (keyup.enter)="enterKeyEvent($event.target.value)" class="form-control text-right w-100"
[disabled]="disabled" (blur)="onBlur($event.target.value)" [(ngModel)]="value" />
Ts
import { Component, OnInit, ViewChild, ElementRef, Input, forwardRef, Output, EventEmitter } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { TranslateService } from '@ngx-translate/core';
export const AMOUNT_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => AmountInputComponent),
multi: true
};
@Component({
selector: 'app-amount-input',
templateUrl: './amount-input.component.html',
styleUrls: ['./amount-input.component.scss'],
providers: [AMOUNT_VALUE_ACCESSOR]
})
export class AmountInputComponent implements OnInit, ControlValueAccessor {
@ViewChild('input')
input: ElementRef;
locale: string;
value: any;
@Input() disabled = false;
@Input() hideClose = true;
@Output() valueChange = new EventEmitter<string>();
@Output() valueBlur = new EventEmitter<string>();
@Output() enterKey = new EventEmitter<string>();
changeCallback = (data: any) => { };
touchCallback = () => { };
constructor(
private translateService: TranslateService
) {
// these should be 'en-US', 'nd-NO','de-DE' etc, should setup on ngx-translate settings
this.locale = this.translateService.currentLang;
}
ngOnInit() {
// **Language selecton and subscription - amountconverter uses this **
this.translateService.onLangChange.subscribe(res => {
this.locale = res.lang;
this.input.nativeElement.value = this.format(this.value);
});
}
onChange(event) {
this.value = event.target.value;
this.changeCallback(event.target.value);
this.valueChange.emit(event.target.value);
}
writeValue(amount: any) {
this.value = amount;
this.input.nativeElement.value = this.format(amount);
this.changeCallback(this.input.nativeElement.value);
this.valueChange.emit(this.input.nativeElement.value);
this.value = this.input.nativeElement.value;
}
registerOnChange(fn: any) {
this.changeCallback = fn;
}
registerOnTouched(fn: any) {
this.touchCallback = fn;
}
setDisabledState(disabled: boolean) {
this.disabled = disabled;
}
format(value: number | string) {
let amount = value ? value.toString() : '0';
amount = amount.replace(/\s/g, '');
amount = amount.replace(/,/g, '');
amount = amount.replace(/\./g, '');
return (Number(amount) / 100).toLocaleString(this.locale, { minimumFractionDigits: 2 });
}
onBlur(value) {
this.writeValue(value);
this.valueBlur.emit(value);
}
enterKeyEvent(value) {
this.enterKey.emit(value);
this.input.nativeElement.value = this.format(value);
}
}
To learn about number conversion refer following link
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat
Following directive will not allow to add unwanted characters to the input. Used in input element of custom component
import { Directive, HostListener, ElementRef } from '@angular/core';
@Directive({
selector: '[appOnlyNumbers]'
})
export class OnlyNumbersDirective {
constructor(private el: ElementRef) { }
@HostListener('keydown', ['$event']) onKeyDown(event) {
const e = <KeyboardEvent>event;
if ([46, 8, 9, 27, 13, 110, 188, 190].indexOf(e.keyCode) !== -1 ||
// Allow: Ctrl+A
(e.keyCode === 65 && e.ctrlKey === true) ||
// Allow: Ctrl+C
(e.keyCode === 67 && e.ctrlKey === true) ||
// Allow: Ctrl+X
(e.keyCode === 88 && e.ctrlKey === true) ||
// Allow: home, end, left, right
(e.keyCode >= 35 && e.keyCode <= 39)) {
// let it happen, don't do anything
return;
}
// Ensure that it is a number and stop the keypress
if ((e.shiftKey || (e.keyCode < 48 || e.keyCode > 57)) && (e.keyCode < 96 || e.keyCode > 105)) {
e.preventDefault();
}
}
}
Usage:
<app-us-amount-input [(ngModel)]="Amount" (valueChange)="itemAmountChange()"></app-us-amount-input>
This can use inside reactive forms too.
<app-us-amount-input formControlName="Amount" (valueChange)="itemAmountChange()"></app-us-amount-input>
Sometimes following function will need to get value of input from component.
format(value: number | string) {
let amount = value ? value.toString() : '0';
amount = amount.replace(/\s/g, '');
amount = amount.replace(/,/g, '');
amount = amount.replace(/\./g, '');
return (Number(amount) / 100);
}
Hope this will help to someone.
来源:https://stackoverflow.com/questions/51474527/format-input-as-number-with-locale-in-angular-6