【问题标题】: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" 访问,它没有这个问题。有些东西正在拦截非数字输入并将其清除。

标签: forms angular validation


【解决方案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(/^\d{9}$/);
            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 或为空时都为真。
          猜你喜欢
          • 2023-03-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-09-01
          • 1970-01-01
          相关资源
          最近更新 更多