在 Angular 6 指令中格式化数字/货币
Formating Number / Currency in Angular 6 Directive
我创建了一个货币指令,我将在每个需要货币格式的输入元素中使用它。
所以我有 2 个主机监听器,一个是 OnFocus,第二个是 Blur
而且效果很好。但是我通过binding
设置input的值的时候需要格式化input的值
所以当我打开一个模式时,我得到了未格式化的值...NgOnInit 不起作用,因为它提早了太多
这是我的指令代码。
import { Directive, HostListener, Input, OnInit, ElementRef, AfterViewInit, OnChanges, Renderer2, ViewChild } from '@angular/core';
import { CurrencyPipe, getCurrencySymbol } from '@angular/common';
import { NgControl, ControlValueAccessor } from '@angular/forms';
import { CustomCurrencyPipe } from '../pipes/custom-currency.pipe';
import { ModalDirective } from 'ngx-bootstrap/modal';
@Directive({
selector: '[appCurencyFormat]',
providers: [CustomCurrencyPipe]
})
export class CurrencyFormatDirective implements OnInit{
//@Input('appNumberFormat') params: any;
@Input() decimalNumber: number = 2;
@Input() symbol: string = "symbol";
//@Input() OnlyNumber: boolean;
local: string;
decimal: string;
currency: string;
element: any;
@ViewChild(ModalDirective) childModal: ModalDirective;
constructor(private elementRef: ElementRef, private ngControl: NgControl, private currencyPipe: CustomCurrencyPipe, private _renderer: Renderer2) {
this.element = this.elementRef.nativeElement;
}
@HostListener('keydown', ['$event']) onKeyDown(event) {
let e = <KeyboardEvent>event;
//190 in array for .
if ([46, 8, 9, 27, 13, 110].indexOf(e.keyCode) !== -1 ||
// Allow: Ctrl+A
(e.keyCode === 65 && (e.ctrlKey || e.metaKey)) ||
// Allow: Ctrl+C
(e.keyCode === 67 && (e.ctrlKey || e.metaKey)) ||
// Allow: Ctrl+V
(e.keyCode === 86 && (e.ctrlKey || e.metaKey)) ||
// Allow: Ctrl+X
(e.keyCode === 88 && (e.ctrlKey || e.metaKey)) ||
// 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();
}
}
@HostListener('focus', ['$event.target.value'])
onFocus(value: any) {
this.ctrl.setValue(this.currencyPipe.convertToNumber(value));
}
@HostListener('blur', ['$event.target.value'])
onBlur(value: any) {
this.ctrl.setValue(this.currencyPipe.transform(value, this.decimalNumber, this.symbol));
}
get ctrl() {
return this.ngControl.control;
}
}
我的解决方案是在 ngOnInit 中设置间隔...
ngOnInit() {
let m = window.setInterval(() => {
console.log("Upao sam");
console.log(this.ctrl.value);
if (this.ctrl.value) {
console.log(this.ctrl.value);
if (seted) {
window.clearInterval(m);
} else {
seted = true;
this.ctrl.setValue(this.currencyPipe.transform(this.ctrl.value, this.decimalNumber, this.symbol));
}
}
}, 500);
}
有谁知道我可以使用哪个 HostListener,尽量避免使用 window.setInterval()
。或者如果有人知道我该如何解决这个问题?
更新
ngOnChanges()
不是每次都提出,所以选的重复问题解决不了我的问题
以下是解决此问题的方法:
<input [ngModel]="item | customCurrencyPipe:'USD':'symbol':'2.2'" name="inputField" type="text"
(ngModelChange)="item = $event||0.00" [ngModelOptions]="{updateOn:'blur'}"/>
1) 将绑定拆分为 one-way 绑定和事件绑定。
2) Event-binding "ngModelChange" 使用提供的输入恢复值。
3) 在 'blur' 上更新值,因此不能提供任何不是数字的文本 // 根据要求可选
4) customCurrencyPipe :这将具有货币管道的默认功能,但如果未提供数字则不会升级,而是 return 默认值或根据要求
,不允许除数字以外的其他内容//
通过上述步骤,您将能够以更少的黑客攻击和有前途的解决方案实现预期的结果。
我会使用整个值访问器。这就是我用于所有输入的内容。你可以这样写
@Component({
selector: 'ks-input',
template: `<input [(ngModel)]="value" />`,
styleUrls: ['./whatever.component.css'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => InputComponent),
multi: true
}
]
})
export class InputComponent implements ControlValueAccessor {
@Input('value') _value = '';
onChange: any = () => {
};
onTouched: any = () => {
};
get value() {
return this._value;
}
set value(val) {
this._value = val;
this.onChange(val);
this.onTouched();
}
registerOnChange(fn) {
this.onChange = fn;
}
registerOnTouched(fn) {
this.onTouched = fn;
}
writeValue(value) {
this.value = value;
}
}
当您将变量传递给此组件时,它将首先通过 writeValue 函数,您可以在其中进行任何初始格式化。您还可以在每次 value/input ngModel 变量更改时调用的设置值函数中执行任何操作。
解决方案是在 ngDoCheck()
中格式化输入值而不是 ngOnChanges()
.
ngDoCheck()
是一个有保证的生命周期挂钩,每次 Angular 触发更改检测时都会 运行。
A small working example 显示 ngDoCheck()
指令的钩子总是在有变化时被触发。
为了理解为什么 ngOnChanges()
不会一直加注以及为什么使用 ngDoCheck()
来代替:
只要指令中的绑定 Input
属性 发生更改,它就会触发 Angular 更改检测。 Angular 如何检测 Input
是否已更改?它比较 Simple Change 对象表示 Input
的旧值和新值,如果它们之间存在差异,则 Angular 触发 ngOnChanges()
生命周期钩子。
所以在某些情况下,您确实输入了一些内容,但比较的值没有改变(例如,您通过 mutate 和 [ 更改了 Input
Object
的 属性,所以实际上 Simple Change 的旧值和新值是相同的,因为 Angular 比较的是引用,而不是值),在这种情况下,它不会引发 ngOnChange()
挂钩。
但是ngDoChek()
是不同的东西。我将使用此信息 from a very good article 关于 ngDoCheck()
:
假设我们有以下 components/directive 树:
Component A
Component B
Component C
所以当 Angular 运行s 变化检测的操作顺序如下:
Checking A component:
- update B input bindings
- call NgDoCheck on the B component
- update DOM interpolations for component A
Checking B component:
- update C input bindings
- call NgDoCheck on the C component
- update DOM interpolations for component B
Checking C component:
- update DOM interpolations for component C
如您所见,只要有变化检测 运行,总是会调用 ngDoCheck()
。除此之外,它还会在初始化 component/directive 时调用(重置 my blitzstack example 以查看)。所以:
格式化输入里面的值ngDoCheck()
就可以解决你的问题
我遇到了和你完全一样的问题。我能够使用 NgControl 钩子从指令访问表单控件来解决它。
我将 NgControl 添加到指令构造函数中:
constructor(
private el: ElementRef,
private decimalPipe: DecimalPipe,
@Self() private ngControl: NgControl) { }
通过这种方式,我能够订阅可观察到的表单控件值更改。用户和代码更改的可观察触发器,因此为了避免在用户键入时格式化我验证控件是原始的:
ngOnInit(): void {
this.ngControl.valueChanges
.subscribe(value => {
if (this.ngControl.pristine) {
this.el.nativeElement.value =
this.decimalPipe.transform(value, `1.2-2`);
}
});
我希望它对其他人有用。我从这篇文章中得到了灵感 https://netbasal.com/attribute-directives-angular-forms-b40503643089
我创建了一个货币指令,我将在每个需要货币格式的输入元素中使用它。
所以我有 2 个主机监听器,一个是 OnFocus,第二个是 Blur
而且效果很好。但是我通过binding
设置input的值的时候需要格式化input的值所以当我打开一个模式时,我得到了未格式化的值...NgOnInit 不起作用,因为它提早了太多
这是我的指令代码。
import { Directive, HostListener, Input, OnInit, ElementRef, AfterViewInit, OnChanges, Renderer2, ViewChild } from '@angular/core';
import { CurrencyPipe, getCurrencySymbol } from '@angular/common';
import { NgControl, ControlValueAccessor } from '@angular/forms';
import { CustomCurrencyPipe } from '../pipes/custom-currency.pipe';
import { ModalDirective } from 'ngx-bootstrap/modal';
@Directive({
selector: '[appCurencyFormat]',
providers: [CustomCurrencyPipe]
})
export class CurrencyFormatDirective implements OnInit{
//@Input('appNumberFormat') params: any;
@Input() decimalNumber: number = 2;
@Input() symbol: string = "symbol";
//@Input() OnlyNumber: boolean;
local: string;
decimal: string;
currency: string;
element: any;
@ViewChild(ModalDirective) childModal: ModalDirective;
constructor(private elementRef: ElementRef, private ngControl: NgControl, private currencyPipe: CustomCurrencyPipe, private _renderer: Renderer2) {
this.element = this.elementRef.nativeElement;
}
@HostListener('keydown', ['$event']) onKeyDown(event) {
let e = <KeyboardEvent>event;
//190 in array for .
if ([46, 8, 9, 27, 13, 110].indexOf(e.keyCode) !== -1 ||
// Allow: Ctrl+A
(e.keyCode === 65 && (e.ctrlKey || e.metaKey)) ||
// Allow: Ctrl+C
(e.keyCode === 67 && (e.ctrlKey || e.metaKey)) ||
// Allow: Ctrl+V
(e.keyCode === 86 && (e.ctrlKey || e.metaKey)) ||
// Allow: Ctrl+X
(e.keyCode === 88 && (e.ctrlKey || e.metaKey)) ||
// 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();
}
}
@HostListener('focus', ['$event.target.value'])
onFocus(value: any) {
this.ctrl.setValue(this.currencyPipe.convertToNumber(value));
}
@HostListener('blur', ['$event.target.value'])
onBlur(value: any) {
this.ctrl.setValue(this.currencyPipe.transform(value, this.decimalNumber, this.symbol));
}
get ctrl() {
return this.ngControl.control;
}
}
我的解决方案是在 ngOnInit 中设置间隔...
ngOnInit() {
let m = window.setInterval(() => {
console.log("Upao sam");
console.log(this.ctrl.value);
if (this.ctrl.value) {
console.log(this.ctrl.value);
if (seted) {
window.clearInterval(m);
} else {
seted = true;
this.ctrl.setValue(this.currencyPipe.transform(this.ctrl.value, this.decimalNumber, this.symbol));
}
}
}, 500);
}
有谁知道我可以使用哪个 HostListener,尽量避免使用 window.setInterval()
。或者如果有人知道我该如何解决这个问题?
更新
ngOnChanges()
不是每次都提出,所以选的重复问题解决不了我的问题
以下是解决此问题的方法:
<input [ngModel]="item | customCurrencyPipe:'USD':'symbol':'2.2'" name="inputField" type="text"
(ngModelChange)="item = $event||0.00" [ngModelOptions]="{updateOn:'blur'}"/>
1) 将绑定拆分为 one-way 绑定和事件绑定。
2) Event-binding "ngModelChange" 使用提供的输入恢复值。
3) 在 'blur' 上更新值,因此不能提供任何不是数字的文本 // 根据要求可选
4) customCurrencyPipe :这将具有货币管道的默认功能,但如果未提供数字则不会升级,而是 return 默认值或根据要求
,不允许除数字以外的其他内容//通过上述步骤,您将能够以更少的黑客攻击和有前途的解决方案实现预期的结果。
我会使用整个值访问器。这就是我用于所有输入的内容。你可以这样写
@Component({
selector: 'ks-input',
template: `<input [(ngModel)]="value" />`,
styleUrls: ['./whatever.component.css'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => InputComponent),
multi: true
}
]
})
export class InputComponent implements ControlValueAccessor {
@Input('value') _value = '';
onChange: any = () => {
};
onTouched: any = () => {
};
get value() {
return this._value;
}
set value(val) {
this._value = val;
this.onChange(val);
this.onTouched();
}
registerOnChange(fn) {
this.onChange = fn;
}
registerOnTouched(fn) {
this.onTouched = fn;
}
writeValue(value) {
this.value = value;
}
}
当您将变量传递给此组件时,它将首先通过 writeValue 函数,您可以在其中进行任何初始格式化。您还可以在每次 value/input ngModel 变量更改时调用的设置值函数中执行任何操作。
解决方案是在 ngDoCheck()
中格式化输入值而不是 ngOnChanges()
.
ngDoCheck()
是一个有保证的生命周期挂钩,每次 Angular 触发更改检测时都会 运行。
A small working example 显示 ngDoCheck()
指令的钩子总是在有变化时被触发。
为了理解为什么 ngOnChanges()
不会一直加注以及为什么使用 ngDoCheck()
来代替:
只要指令中的绑定 Input
属性 发生更改,它就会触发 Angular 更改检测。 Angular 如何检测 Input
是否已更改?它比较 Simple Change 对象表示 Input
的旧值和新值,如果它们之间存在差异,则 Angular 触发 ngOnChanges()
生命周期钩子。
所以在某些情况下,您确实输入了一些内容,但比较的值没有改变(例如,您通过 mutate 和 [ 更改了 Input
Object
的 属性,所以实际上 Simple Change 的旧值和新值是相同的,因为 Angular 比较的是引用,而不是值),在这种情况下,它不会引发 ngOnChange()
挂钩。
但是ngDoChek()
是不同的东西。我将使用此信息 from a very good article 关于 ngDoCheck()
:
假设我们有以下 components/directive 树:
Component A
Component B
Component C
所以当 Angular 运行s 变化检测的操作顺序如下:
Checking A component:
- update B input bindings
- call NgDoCheck on the B component
- update DOM interpolations for component A
Checking B component:
- update C input bindings
- call NgDoCheck on the C component
- update DOM interpolations for component B
Checking C component:
- update DOM interpolations for component C
如您所见,只要有变化检测 运行,总是会调用 ngDoCheck()
。除此之外,它还会在初始化 component/directive 时调用(重置 my blitzstack example 以查看)。所以:
格式化输入里面的值ngDoCheck()
就可以解决你的问题
我遇到了和你完全一样的问题。我能够使用 NgControl 钩子从指令访问表单控件来解决它。
我将 NgControl 添加到指令构造函数中:
constructor(
private el: ElementRef,
private decimalPipe: DecimalPipe,
@Self() private ngControl: NgControl) { }
通过这种方式,我能够订阅可观察到的表单控件值更改。用户和代码更改的可观察触发器,因此为了避免在用户键入时格式化我验证控件是原始的:
ngOnInit(): void {
this.ngControl.valueChanges
.subscribe(value => {
if (this.ngControl.pristine) {
this.el.nativeElement.value =
this.decimalPipe.transform(value, `1.2-2`);
}
});
我希望它对其他人有用。我从这篇文章中得到了灵感 https://netbasal.com/attribute-directives-angular-forms-b40503643089