我正在使用 Angular Reactive 表单创建表单,出于用户体验原因,我尝试在
<input type="number">
字段中插入某些无效值(例如负数或非整数)时禁用表单的提交。
问题是,当我插入一个值时,即使
<input type="number">
允许,通常也会计算为 NaN
,例如,单独的字母 e
、后面没有数字的减号或加号,或者即使是太大而无法用 TypeScript number
表示的数字,在通过 null
或 control.value
访问该值时,也会计算为 control.getRawValue()
。
编辑:当输入字段为空时,表单也需要可提交。遗憾的是,这是一个不可协商的要求,而且我也不能在该字段中使用像
0
这样的默认值。
这是我尝试过的最小可重现示例(它仅尝试阻止 NaN 值)。
app.component.ts
import { Component } from '@angular/core';
import { AbstractControl, FormBuilder, ValidatorFn } from '@angular/forms';
@Component({
selector: 'app-root',
template: `
<form [formGroup]="formGroup" (ngSubmit)="print()">
<input type="number" name="control" id="control" formControlName="control" >
<button type="submit" [disabled] ="formGroup.invalid">print</button>
</form>
`
})
export class AppComponent {
title = 'ReactiveFormNumberControl';
formGroup
constructor(private _fb: FormBuilder) {
this.formGroup = this._fb.group({
control: this._fb.control<number | null>(null, {
validators: [noNaNValidator]
})
})
}
print = () => {
console.log(this.formGroup.value)
}
}
const noNaNValidator: ValidatorFn = (
control: AbstractControl<number | null>,
) => {
const value = control.value
if (Number.isNaN(value)) {
// Value is null and not NaN, so this is never reached because Number.isNaN(null) is false,
// the control (and group) is marked valid and the submit button is not disabled
return {
invalidValue: {
value: value
},
}
}
return null
}
要检查某个值是否为
NaN
,您应该首先将该值转换为数字(如果可能),然后使用方法Number.isNaN()
,在您的示例中您可以尝试:
const value: any = control.value;
const parsedValue = parseFloat(value);
if (Number.isNaN(parsedValue)) {
// Value is null and not NaN, so this is never reached because Number.isNaN(null) is false,
// the control (and group) is marked valid and the submit button is not disabled
return {
invalidValue: {
value: value
}
}
}
在 GitHub 上查看与此相关的 Angular 问题时,更准确地说是Issue #2962,我找到了一种解决方法,利用 HTML5 的
badInput
属性存在于某些类型的 <input>
元素(包括 number
)中。
我编写了一个自定义验证器指令,类似于 在 Angular Issue #2962 的评论中显示的指令,但与该指令不同的是,我编写的指令必须显式应用,并且目前只能应用于
<input type="number">
字段(但如果需要,通过更改指令的 selector
应该很容易更改)。
错误输入验证器.directive.ts
import { Directive, ElementRef } from '@angular/core';
import { AbstractControl, NG_VALIDATORS, ValidationErrors, Validator } from '@angular/forms';
@Directive({
selector: 'input[type=number][appBadInputValidator]',
standalone: true,
providers: [{
provide: NG_VALIDATORS,
useExisting: BadInputValidatorDirective,
multi: true,
}]
})
export class BadInputValidatorDirective implements Validator {
private onChangeCallback?: () => void;
constructor(private _element: ElementRef) { }
validate(_: AbstractControl): ValidationErrors | null {
const inputElement = this._element.nativeElement
return inputElement.validity.badInput ? { 'badInput': true } : null
}
registerOnValidatorChange?(fn: () => void): void {
this.onChangeCallback = fn
}
}
这可能可以以某种方式改进,但目前它满足了我的需要。当我将
appBadInputValidator
应用于 <input type="number">
元素,并输入一个将解析为 NaN
的值时,FormControl
和 FormGroup
被标记为无效,并且提交按钮被正确禁用。
非常感谢 GitHub 上的 amc1804 发布解决方案。