根据 <input type=“number” 的 NaN 值进行验证>

Validate against NaN values for <input type="number">

提问人:FaBEEola 提问时间:10/17/2023 最后编辑:FaBEEola 更新时间:10/18/2023 访问量:94

问:

我正在使用 Angular Reactive 表单创建一个表单,出于 UX 原因,当在字段中插入某些无效值(例如负数或非整数)时,我尝试禁用表单的提交。<input type="number">

问题是,当我插入一个值时,即使允许,通常也会计算为 ,例如,单独的字母、后面没有数字的减号或加号,甚至是一个太大而无法用 TypeScript 表示的数字,而是在通过 或 访问值时计算为 。<input type="number">NaNenumbernullcontrol.valuecontrol.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
}
angular typescript angular-reactive-forms nan angular-custom-validators

评论


答:

0赞 Matthieu Riegler 10/17/2023 #1

NumberValueAccessor 负责将每个无效值转换为 .null

这是预期行为。

评论

0赞 FaBEEola 10/17/2023
查看 NumberValueAccessor 的文档和源代码是有意义的。编写扩展 NumberValueAccessor 的自定义值访问器指令是否可以成为我特定用例的解决方案?或者那只是打开一个全新的蠕虫罐头?
0赞 Matthieu Riegler 10/17/2023
NumberValueAccessor在我不确定这是否适用于让 2 个指令匹配相同的输入!FormsModule
0赞 FaBEEola 10/17/2023
在这种情况下,我可能会在 GitHub 上向 Angular 团队提出功能请求,看看有条件地允许 NaN/无效值是否对其他用户有意义。谢谢你的解释。
0赞 FaBEEola 10/18/2023
在 GitHub 上查看 Angular 问题时,我发现了一个解决方法。请看我自己对这个问题的回答。
0赞 Hlib Sheremet 10/17/2023 #2

要检查一个值是否为 ,如果可能的话,您应该首先将该值转换为数字,然后使用 方法 ,在您的示例中,您可以尝试:NaNNumber.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
      }
    }
  }

评论

0赞 FaBEEola 10/17/2023
虽然这确实使表单在我尝试键入 NaN 值时无效,但它也具有不良的副作用,即当字段为空时使表单无效。这在原始问题中没有具体说明(我将更新),所以这是我的责任。
0赞 Hlib Sheremet 10/17/2023
顺便说一句,您可以简化在类似的东西旁边添加第二个条件,只有当字段中存在某些值时才会检查它Number.isNaN(parsedValue)parsedValue && Number.isNaN(parsedValue)
0赞 FaBEEola 10/17/2023
我尝试按照您的建议执行操作,但是如果我尝试无效的输入,例如 或 ,提交按钮仍处于启用状态,就像我最初的示例一样。e-
0赞 FaBEEola 10/18/2023 #3

在 GitHub 上查看与此相关的 Angular 问题时,更准确地说是问题 #2962,我发现了一种解决方法,利用某些类型的元素中存在的 HTML5 属性。badInput<input>number

我编写了一个自定义验证器指令,类似于 Angular Issue #2962 评论中显示的指令,但与该指令不同的是,我编写的指令必须显式应用,并且现在只能应用于字段(但如果需要,通过更改指令的 )。<input type="number">selector

bad-input-validator.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">NaNFormControlFormGroup

该指令的工作方式类似于 Angular.JS 的表单验证,它跟踪 ,但此行为目前在 Angular 中不存在,至少从 16.2.6 版本开始。badInput

非常感谢 GitHub 上的 amc1804 发布解决方案。