输入字段只接受数字而不在输入其他字符时触发事件

Input field accepting only numbers without firing events when input other caracters

我对 html 数字输入的默认行为不满意,我想要一个只接受数字的简单输入。

我创建了这个指令:

import { Directive, ElementRef, HostListener, Input } from '@angular/core';
import { NgControl } from '@angular/forms';

@Directive({
  selector: 'input[numbersOnly]'
})
export class NumberDirective {

  constructor(private _el: ElementRef) { }

  @HostListener('input', ['$event']) onInputChange(event) {
    const initalValue = this._el.nativeElement.value;
    this._el.nativeElement.value = initalValue.replace(/[^0-9]*/g, '');
    if ( initalValue !== this._el.nativeElement.value) {
      event.stopPropagation();
    }
  }

}

我面临的问题是 "event.stopPropagation()" 不会停止事件传播,即使从用户的角度来看输入值没有改变,也会触发事件。

如何在字段值未更改时停止事件传播?

编辑: 为了更好地理解,这里有一个 stackblitz 示例: https://stackblitz.com/edit/angular-numbers-only-directive

我真的不认为有什么方法可以使用带有 HostListener 的指令来停止此事件。

Angular 在调用侦听器之前检测到此事件,因此停止传播不会影响 Angular 事件,例如您在父组件中做出反应的 ngModelChange(没有证据证明我可能是错的)

我建议提供自己的事件指示值更改,并在您真正希望它发出时发出它:

export class NumberDirective {
  @Output() numberChanged = new EventEmitter<number>();

  constructor(private _el: ElementRef) { }

  @HostListener('input', ['$event']) onInputChange(event) {
    const initalValue = this._el.nativeElement.value;
    this._el.nativeElement.value = initalValue.replace(/[^0-9]*/g, '');
    if ( initalValue === this._el.nativeElement.value) {
      this.numberChanged.emit(initalValue);
    }
  }

}

然后你可以用numberChanged替换ngModelChanged绑定:

<input type="text" [ngModel]="value" (numberChanged)="onChange($event)" numbersOnly/>

演示: https://stackblitz.com/edit/angular-numbers-only-directive-a2mv6e

为了在响应式表单中使用,我最终制作了一个实现 ControlValueAccessor 的自定义组件:

组件声明(摘录):

@Component({
  selector: 'number',
  template: `
    <input type="text" [(ngModel)]="value" numbersOnly/>
  `,
  ...
  ]
})
export class NumberInputComponent implements ControlValueAccessor {
  ...

  set value(value) {
    const tmp = value.replace(/[^0-9]*/g, '');
    if(tmp && tmp !== '' && +tmp !== +this._value) {
      this._value = +tmp;
      this.onChange(this._value);
    }
  }

  ...
}

用法:

<number [formControl]="formControl"></number>

Here is the stackblitz.