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 pipes
、modelValue
、$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的主要内容,如果未能解决你的问题,请参考以下文章