输入类型号“仅数值”验证

Posted

技术标签:

【中文标题】输入类型号“仅数值”验证【英文标题】:Input type number "only numeric value" validation 【发布时间】:2017-12-16 21:28:54 【问题描述】:

我如何验证 type="number" 的输入仅在值是数字或 null 时才有效,仅使用响应式表单(无指令)? 只有数字 [0-9] 和 .是允许的,没有“e”或任何其他字符。


到目前为止我所做的尝试:

模板:

<form [formGroup]="form" novalidate>
    <input type="number" formControlName="number" id="number">
</form>

组件:

export class App 
  form: FormGroup = new FormGroup();

  constructor(
    private fb: FormBuilder,
  ) 
    this.form = fb.group(
      number: ['', [CustomValidator.numeric]]
    )
  

自定义验证器:

export class CustomValidator
  // Number only validation
  static numeric(control: AbstractControl) 
    let val = control.value;

    if (val === null || val === '') return null;

    if (!val.toString().match(/^[0-9]+(\.?[0-9]+)?$/)) return  'invalidNumber': true ;

    return null;
  

Plunker

问题是当用户输入不是数字的内容时(“123e”或“abc”) FormControl 的值变为null,请记住我不想要该字段是必需的,因此如果该字段确实为空,null 值应该是有效的。

跨浏览器支持也很重要(Chrome 的数字输入字段不允许用户输入字母 - “e”除外,但 FireFox 和 Safari 可以)。

【问题讨论】:

你有没有找到一个可以接受的答案?当我决定从type="text" 切换到type="number" 时,我遇到了同样的问题。我不明白为什么 Angular 会更改控件的值,但对有效性没有做任何事情。对我来说似乎是一个错误。 对于所描述的确切情况,没有。不幸的是…… 所以我还没有实现这个,但我计划实现一个自定义ControlValueAccessor 来克服这种行为。我已经有一个自定义日期控件,它执行类似于将符合 ISO 标准的日期字符串存储在表单值中的操作,而不是输入中显示的“10/16/2018”字符串。一旦我希望在本月的某个时候遇到关于 type="number" 的错误,我将在这里添加我的解决方案。 我也有这个问题。 AngularJS 有一个内置的数字验证器,可以使用 ng-messages="number" 访问,它没有这个问题。有些东西正在拦截非数字输入并将其清除。 github.com/angular/angular/issues/11644 描述了该问题。此处引用:github.com/angular/angular/issues/2962 【参考方案1】:

html 文件中,您可以像这样为您的模式添加 ngIf,

<div class="form-control-feedback" *ngIf="Mobile.errors && (Mobile.dirty || Mobile.touched)">
        <p *ngIf="Mobile.errors.pattern" class="text-danger">Number Only</p>
      </div>

.ts 文件中,您可以添加验证器模式 -"^[0-9]*$"

this.Mobile = new FormControl('', [
  Validators.required,
  Validators.pattern("^[0-9]*$"),
  Validators.minLength(8),
]);

【讨论】:

要求该字段可以为空,数字类型,不使用指令,如果它真的不为空,则只允许数字 0-9。请提供您的解决方案的工作预览,这个答案真的不是。

仅限数字

@mihailo 你在 *ngif 中添加了 mobile.errors.required 所以,它不允许空值。 对为什么有这么多赞成票感到困惑。它没有完全回答这个问题。原始海报使用 【参考方案2】:

使用指令变得简单,可以在整个应用程序中使用

HTML

<input type="text" placeholder="Enter value" numbersOnly>

由于 .keyCode().which() 已弃用,代码使用 .key() 检查 转介from

指令:

@Directive(
   selector: "[numbersOnly]"
)

export class NumbersOnlyDirective 
  @Input() numbersOnly:boolean;

  navigationKeys: Array<string> = ['Backspace']; //Add keys as per requirement
  
  constructor(private _el: ElementRef)  

  @HostListener('keydown', ['$event']) onKeyDown(e: KeyboardEvent) 
    
    if (
      // Allow: Delete, Backspace, Tab, Escape, Enter, etc
      this.navigationKeys.indexOf(e.key) > -1 || 
      (e.key === 'a' && e.ctrlKey === true) || // Allow: Ctrl+A
      (e.key === 'c' && e.ctrlKey === true) || // Allow: Ctrl+C
      (e.key === 'v' && e.ctrlKey === true) || // Allow: Ctrl+V
      (e.key === 'x' && e.ctrlKey === true) || // Allow: Ctrl+X
      (e.key === 'a' && e.metaKey === true) || // Cmd+A (Mac)
      (e.key === 'c' && e.metaKey === true) || // Cmd+C (Mac)
      (e.key === 'v' && e.metaKey === true) || // Cmd+V (Mac)
      (e.key === 'x' && e.metaKey === true) // Cmd+X (Mac)
    ) 
        return;  // let it happen, don't do anything
    
    // Ensure that it is a number and stop the keypress
    if (e.key === ' ' || isNaN(Number(e.key))) 
      e.preventDefault();
    
  

【讨论】:

【参考方案3】:

最简单的方法是使用像 one 这样的库,特别是您希望 noStrings 为真

    export class CustomValidator   // Number only validation   
      static numeric(control: AbstractControl) 
        let val = control.value;

        const hasError = validate(val: val, val: numericality: noStrings: true);

        if (hasError) return null;

        return val;   
       
    

【讨论】:

一旦输入包含字母,FormControls 值为 null,我该如何实现? 我编辑了答案,让您了解如何做到这一点 是的,谢谢,但这并不能解决问题的核心。如果输入包含字母let val = control.value = null。这是一个问题,当它是数字类型时,角度如何解析输入值。如果输入是 text 类型,我的验证将完美运行。【参考方案4】:

我也遇到了类似的问题:我想要一个不需要的输入字段上的数字和 null。通过许多不同的变化工作。我终于选择了这个,这似乎可以解决问题。您将指令ntvFormValidity 放置在任何具有本机无效性且不会将该无效状态转换为 ng-invalid 的表单控件上。

示例使用: &lt;input type="number" formControlName="num" placeholder="0" ntvFormValidity&gt;

指令定义:

import  Directive, Host, Self, ElementRef, AfterViewInit  from '@angular/core';
import  FormControlName, FormControl, Validators  from '@angular/forms';

@Directive(
  selector: '[ntvFormValidity]'
)
export class NtvFormControlValidityDirective implements AfterViewInit 

  constructor(@Host() private cn: FormControlName, @Host() private el: ElementRef)  

  /* 
  - Angular doesn't fire "change" events for invalid <input type="number">
  - We have to check the DOM object for browser native invalid state
  - Add custom validator that checks native invalidity
  */
  ngAfterViewInit() 
    var control: FormControl = this.cn.control;

    // Bridge native invalid to ng-invalid via Validators
    const ntvValidator = () => !this.el.nativeElement.validity.valid ?  error: "invalid"  : null;
    const v_fn = control.validator;

    control.setValidators(v_fn ? Validators.compose([v_fn, ntvValidator]) : ntvValidator);
    setTimeout(()=>control.updateValueAndValidity(), 0);
  

挑战是从 FormControl 中获取 ElementRef 以便我可以检查它。我知道有@ViewChild,但我不想用 ID 注释每个数字输入字段并将其传递给其他东西。所以,我构建了一个指令,它可以请求 ElementRef。

在 Safari 上,对于上面的 HTML 示例,Angular 将表单控件标记为对“abc”等输入无效。

我认为,如果我要重新执行此操作,我可能会为数字输入字段构建自己的 CVA,因为这样可以提供更多控制并制作简单的 html。

类似这样的:

&lt;my-input-number formControlName="num" placeholder="0"&gt;

PS:如果有更好的方法来获取指令的 FormControl,我猜想在声明中使用依赖注入和providers,请告诉我,以便我可以更新我的指令(以及这个答案)。

【讨论】:

【参考方案5】:

尽量减少输入,只允许从 0 到 9 的数字。这在 Angular Cli 中对我有用

<input type="number" oninput="this.value=this.value.replace(/[^\d]/,'')"  min=0>

【讨论】:

【参考方案6】:

进行数字验证的最简单和最有效的方法是(它也会限制空格和特殊字符)

如果你不想限制长度,你可以删除 maxlength 属性

HTML

<input type="text" maxlength="3" (keypress)="validateNo($event)"/>

TS

validateNo(e): boolean 
    const charCode = e.which ? e.which : e.keyCode;
    if (charCode > 31 && (charCode < 48 || charCode > 57)) 
      return false
    
    return true

【讨论】:

【参考方案7】:

您需要在自定义验证器中使用正则表达式。例如,下面的代码只允许输入字段中包含 9 位数字:

function ssnValidator(control: FormControl): [key: string]: any 
  const value: string = control.value || '';
  const valid = value.match(/^\d9$/);
  return valid ? null : ssn: true;

在此处查看示例应用:

https://github.com/Farata/angular2typescript/tree/master/Angular4/form-samples/src/app/reactive-validator

【讨论】:

或者简单地用let regex = /\D+/; regex.exec("stringToT3est")测试它。如果它不包含数字,它将返回 null。但这不适用于十进制符号。 我试过使用正则表达式,但这不是问题的核心。当输入是数字类型并且值不是数字(“123e”或“abc”)时,FormControl 的值变为null... 好的,我现在明白你的问题了,这与 CustomValidator 内部的逻辑无关,而是如何首先获取输入而不是 null。是否可以将您的输入从类型编号更改为类型文本(或简单地将其删除并默认为文本)并以这种方式验证它?然后用户可以输入任何字符串,但验证器可以拒绝它 到目前为止我就是这样解决它的,但是当输入是 text 类型时,我失去了绑定到 number 类型输入的浏览器功能.主要是输入在移动设备上的作用方式。 恐怕对于这个用例,您必须将其保留为文本,如果您想要数字增量功能,构建自定义控件并不难。在 HTML 规范中,数字的输入文本将返回 null 值,例如“2e”,无法访问原始值:html.spec.whatwg.org/multipage/…【参考方案8】:

有时尝试这样简单的事情会更容易。

validateNumber(control: FormControl):  [s: string]: boolean  

  //revised to reflect null as an acceptable value 
  if (control.value === null) return null;

  // check to see if the control value is no a number
  if (isNaN(control.value)) 
    return  'NaN': true ;
  

  return null; 

希望这会有所帮助。

根据评论更新, 你需要像这样调用验证器

number: new FormControl('',[this.validateNumber.bind(this)])

如果您将验证器放入组件中,则必须使用 bind(this),这就是我的做法。

【讨论】:

你测试了吗?当我在CustomValidator (在我的 Plunker 中) 中使用 id 时,这不会验证 我让它在我的代码中运行,但没有放入 plunker,我会在几分钟后尝试设置示例,但我确实更新了答案以包含模板 请记住,问题的核心是 Firefox 和其他一些行为的方式。如果您输入了数字并且输入了"123e" 这不是数字并且输入的值是null 但我认为如果它的值是null 则输入有效。我发现唯一 100% 成功的方法是监听输入事件并检查键 + 复制/粘贴验证。 如果您希望 null 有效,那么只需检查 if (control.value === null) return null; 到允许 null 正常的代码。如果这是可以接受的,那么我将更改代码。 control.value === null 将在输入有123e 或为空时都为真。

以上是关于输入类型号“仅数值”验证的主要内容,如果未能解决你的问题,请参考以下文章

Android中验证输入是否为汉字及手机号,邮箱验证,IP地址可用port号验证

HTML5 输入类型号与电话

查找验证错误的错误号

输入类型文件无法使用 bootstrapvalidator 进行验证

小米5查看设备号信息及验证type-c数据线

输入类型不是“提交”时的 HTML5 验证