Angular 使用带有@HostListener 的指令来更新 ReactiveForm 的输入值是将输入设置为 ngValid 而不是 ngInvalid

Angular Using Directive with @HostListener to Update Input Value of ReactiveForm is Setting Input to ngValid Instead of ngInvalid

我在 ReactiveForm FormControl 上放置了一个货币属性指令,它在输入事件 (onKeyDown) 上使用 @HostListener 来删除所有无效字符(字母和符号)因为它们被输入到输入中,但允许数字和小数。 BUT,如果您在空的输入字段中键入无效字符(即 a)并且它被指令删除,则模型 不是 已更新。

我使用货币指令添加了 plunker 设置。理解我的问题的步骤:

  1. 键入 123a 您没有在输入中得到 a,因为不允许输入任何字母,并且由于表单无效,按钮被禁用 (好)
  2. 键入 123.456 您没有在输入中得到 6 因为只允许 2 位小数,并且由于表单无效而禁用了按钮 (good )
  3. 键入 a 你没有在输入中得到 a,但是 butt 已启用,因为模型认为它有一个 a,即使 UI 不显示 (差)

您可以通过单击按钮并在控制台中查看记录 this.form.value 并显示 { amount: 'a' } 来验证模型未更新。如果您接下来键入一个有效字符,该模型将仅包含该字符,并且 a 将被删除。所以只有在这种情况下,模型没有正确更新。

这个问题在 AngularJS 中很容易解决,使用 ngModel validator and parser pipesmodelValue$setViewValue$render() 更新并强制 AngularJS 到 运行 一个 $digest。在 Angular 中如何做到这一点?

这是我的属性指令中的一个片段,它成功地删除了不需要的字符:

@HostListener('input', ['$event'])
  onKeyDown(event: KeyboardEvent) {
    const input = event.target as HTMLInputElement;

    // Only numbers and decimals
    let trimmed = input.value.replace(/[^\d\.,]+/g, '');

    // Only a single decimal and choose the first one found
    if (trimmed.split('.').length > 2) {
      trimmed = trimmed.replace(/\.([^\.]*)$/, '');
    }

    // Cannot start with decimal typed or pasted
    if (trimmed.indexOf('.') === 0) { trimmed = ''; }

    // AngularJS "like" solution would be something like:
    // ngModelCtrl.$setViewValue(trimmed);
    // ngModelCtrl.$render();
    // Angular solution is???

    input.value = trimmed;
}

最近的 angular 4 版本引入了一个新指令来处理这些情况

<input [name]="fullName" pattern="[a-zA-Z ]*" [(ngModel)]="...">

在模式中你可以指定任何正则表达式

Docs

更新:根据评论,如果您试图限制用户输入字符,您可以使用自定义指令。

为你的指令函数使用下面的代码

  @HostListener('keydown') onKeydown() {
      let value= this.el.nativeElement.value;
     let key= value.charCodeAt(value.length -1])
     let strippedString ='';
     if(!((key > 64 && key < 91) || (key> 96 && key< 123) || key== 8 || key== 32 || (key>= 48 && key<= 57)){
       strippedString = this.el.nativeElement.value.substring(0,value.length-1)
       this.el.nativeElement.value = strippedString
     }

LIVE DEMO

所以我确实找到了一个解决方案,使用 NgControl 注入 private ngControl: NgControl 然后访问它的控件 属性 this.ngControl.control.patchValue(newValue);,它更新输入字段模型我的 onKeyDown 活动中的 ReactiveForm - 请参阅 plunker

BUT 基于智能组件和哑组件的使用 EventEmitter 实际上是一个更好的解决方案,它将值从输入 - plunker(感谢 Todd Motto 和他的 Ultimate Angular 课程)