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

Posted

技术标签:

【中文标题】Angular 使用带有 @HostListener 的指令来更新 ReactiveForm 的输入值是将输入设置为 ngValid 而不是 ngInvalid【英文标题】:Angular Using Directive with @HostListener to Update Input Value of ReactiveForm is Setting Input to ngValid Instead of ngInvalid 【发布时间】:2017-09-08 20:59:57 【问题描述】:

我在 ReactiveForm FormControl 上组合了一个货币属性指令,该指令在输入事件 (onKeyDown) 上使用 @HostListener 来删除所有输入到输入中的无效字符(字母和符号),但允许数字和小数。 但是,如果您在空输入字段中输入无效字符(即a)并被指令删除,则模型不会更新。 p>

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

    输入123a你没有在输入中得到a,因为不允许使用字母,并且由于表单无效,按钮被禁用(好) 输入123.456你没有得到6,因为只允许小数点后2位,并且由于表单无效,按钮被禁用(好) 键入a,你在输入中没有得到a,但是由于模型认为它有一个a,即使UI没有显示它(不好)

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

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

这是我的属性指令中的一个 sn-p,它成功地修剪掉了不需要的字符:

@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(/\.([^\.]*)$/, '$1');
    

    // 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;

【问题讨论】:

【参考方案1】:

在最近的 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

【讨论】:

感谢您的回复。模式验证器仅在出现问题时才说,但我阻止输入值,而不仅仅是警告它不正确。模式实际上从 ng1 就已经存在并且也在 ng2 中。 是的,我在问题中说我正在使用属性指令,这就是我在问题末尾添加的使用 @HostListener 的代码 sn-p 的来源。 嗨,我想你会发现我的 sn-p 会从输入中剥离值。为了澄清我在问什么,我添加了一个 plunker。 我不明白你的问题 所以你不想要这个?【参考方案2】:

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

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

【讨论】:

以上是关于Angular 使用带有 @HostListener 的指令来更新 ReactiveForm 的输入值是将输入设置为 ngValid 而不是 ngInvalid的主要内容,如果未能解决你的问题,请参考以下文章

如何在带有 D3 的 Angular 中使用“this”?

在带有 @angular/cdk-experimental 的 Angular Material 2 表中使用虚拟滚动

带有 Express js 的 Angular 2 cli

带有快速项目的 angular-cli

带有发布请求的 Angular 6 标头不起作用

如何在 Angular 中使用带有 SASS 的 Bootstrap 4