当输入动态更新时如何检测它的值变化 (Angular 6)

3
我正在使用自定义指令和自定义管道对文本输入进行货币格式化。它可以很好地处理任何类型的直接用户输入(聚焦、失焦、按键)。然而,当值被动态更改时,我似乎无法捕获change事件。我也找不到可靠的HostListener事件列表,并且不知道如何捕获输入的任何事件(因此无法查看发生了哪个事件,如果有的话)。
在动态情况下,值是通过patchValue设置的,我已将emitEvent设置为true,但这似乎没有任何作用(我认为默认情况下它已经是true了):
myInput.patchValue({content: currentContent}, { emitEvent: true });

在使用patchValue设置内容值之前,我可以重写货币格式,但这会影响到可重用性。

以下是我的指令:

import { Directive, HostListener, ElementRef, OnInit } from '@angular/core';
import { CurrencyPipe } from '../pipes/currency.pipe';

@Directive({
    selector: '[appCurrency]'
})
export class CurrencyDirective implements OnInit {

constructor(
    private elementRef:ElementRef,
    private formatcurrencypipe:CurrencyPipe
) { }

ngOnInit(){
    //this.elementRef.nativeElement.value = this.formatcurrencypipe.transform(this.elementRef.nativeElement.value);
}

@HostListener("change", ["$event.target.value", "$event"]) onChange(value, event) {
    //this.elementRef.nativeElement.value = this.formatcurrencypipe.parse(value);
}

@HostListener("valueChange", ["$event.target.value", "$event"]) onValueChange(value, event) {
    console.log('in onValueChange');
    //doesn't trigger when the value is changed dynamically
}

@HostListener("focus",["$event.target.value","$event"]) onFocus(value,event) {
    console.log('in focus');
    this.elementRef.nativeElement.value = this.formatcurrencypipe.parse(value);
    if(event.which == 9)
    {
        return false;
    }
    this.elementRef.nativeElement.select();
}

@HostListener("blur", ["$event.target.value"]) onBlur(value) {
    console.log('in blur');
    this.elementRef.nativeElement.value = this.formatcurrencypipe.transform(value);
}

@HostListener('keydown', ['$event']) onKeyDown(event) {
    let e = <KeyboardEvent> event;
    console.log('e.keyCode: ', e.keyCode, e.ctrlKey, e.metaKey);
    //delete, backspace, tab, escape, enter, decimal, period, arrow left, arrow right
    if ([46, 8, 9, 27, 13, 110, 190, 37, 39].indexOf(e.keyCode) !== -1
    || (e.keyCode === 65 && (e.ctrlKey || e.metaKey)) //CTRL + A
    || (e.keyCode === 67 && (e.ctrlKey || e.metaKey)) //CTRL + C
    || (e.keyCode === 86 && (e.ctrlKey || e.metaKey)) //CTRL + V
    || (e.keyCode === 88 && (e.ctrlKey || e.metaKey))) {  //CTRL + X
        //do nothing
        return;
    }

    // Check for number
    if ((e.shiftKey || (e.keyCode < 48 || e.keyCode > 57)) && (e.keyCode < 96 || e.keyCode > 105)) {
        e.preventDefault();
    }
}
}

我已经在这里添加了stackblitz:https://stackblitz.com/edit/angular-tys9cy

你尝试过使用 <input class="name" (change)="onChange($event)"> 吗? - NoHoney_k1ll
我刚刚尝试了一下,它什么也没做。我可能没有使用正确的 HostListener 事件去捕获它。 - Katharine Osborne
我已经添加了 StackBlitz。 - Katharine Osborne
1
@KatharineOsborne 【Forked StackBlitz】(https://stackblitz.com/edit/angular-g8zsmt)请查看。 - Vikas
@KatharineOsborne 看看我的答案。 - Vikas
显示剩余7条评论
2个回答

5

响应式表单实例如FormGroupFormControl有一个valueChanges方法,返回一个发出最新值的observable。它不会发出DOM事件。
解决方案

而不是绑定到valueChange,绑定到ngModelChange,这将在两种情况下触发,即当formControl在视图中更新或通过模型更新时。

@HostListener("ngModelChange", [ "$event"]) onNgModelChange(value) {
         console.log(value)
    this.elementRef.nativeElement.value = this.formatcurrencypipe.transform(value);
}

Working StackBlitz


ngModelChangekeydown 之后触发,因此在再次转换之前您无法连续输入多个按键。 - Katharine Osborne
@KatharineOsborne 如果你无法找到解决方案,可以尝试使用Rxjs debounceTime。如果需要帮助,请告诉我。 - Vikas
是的,我无法弄清如何使用debounceTime。我也不确定它是否适用,因为用户可能会在完成输入之前暂停(在项目的上下文中可能需要进行很多心理计算)。我设置了一个数据值来捕获焦点状态,而当它这样做时,我无法正确地应用管道。总之,感觉非常不符合Angular的风格。 - Katharine Osborne
天啊,我刚意识到我在失焦和聚焦时都将我的焦点标志设置为 true。扔脸掌 - Katharine Osborne

0

基于 @vikas 的答案,我进行了修改,使其仅在值动态设置时使用 ngModelChange 进行更新,而不是在用户编辑时也进行更新(因为 ngModelChange 对我所需的内容过于包容):

@HostListener("ngModelChange", [ "$event"]) onNgModelChange(value) {
    //when value changes dynamically
    if (this.elementRef.nativeElement.dataset.isfocused == 'false') {
        console.log('is not focused');
        this.elementRef.nativeElement.value = this.formatcurrencypipe.transform(value);
    } else {
        console.log('is focused');
    }
}

@HostListener("focus",["$event.target.value","$event"]) onFocus(value,event) {
    this.elementRef.nativeElement.dataset.isfocused = true;
    console.log('isfocused: ', this.elementRef.nativeElement.dataset);
    this.elementRef.nativeElement.value = this.formatcurrencypipe.parse(value);
    if(event.which == 9)
    {
        return false;
    }
    this.elementRef.nativeElement.select();
}

@HostListener("blur", ["$event.target.value"]) onBlur(value) {
    this.elementRef.nativeElement.dataset.isfocused = false;
    this.elementRef.nativeElement.value = this.formatcurrencypipe.transform(value);
}

还有输入:

<input appCurrency data-isfocused="false" type="text" class="form-control number" formControlName="myInput" />

使用数据属性似乎与Angular不太兼容 :-(


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接