输入类型号“只有数值”的验证

如何验证 type="number"的输入仅在值为数字或空时才有效,只能使用 Reactive Forms(没有指令)
只允许使用数字 [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]]
})
}
}

CustomValidator:

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;
}
}

笨蛋

问题是当一个用户输入的东西不是一个数字 (“123e”或“ abc”)的表单控制的值变成 null,记住,我不希望字段是必需的,所以如果字段真的是空的 null值应该是有效的。

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

175081 次浏览

The easiest way would be to use a library like this one and specifically you want noStrings to be true

    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;
}
}

You need to use regular expressions in your custom validator. For example, here's the code that allows only 9 digits in the input fields:

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

Take a look at a sample app here:

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

Sometimes it is just easier to try something simple like this.

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;
}

Hope this helps.

updated as per comment, You need to to call the validator like this

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

The bind(this) is necessary if you are putting the validator in the component which is how I do it.

In the HTML file, you can add ngIf for your pattern like this,

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

In .ts file you can add the Validators pattern -"^[0-9]*$"

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

I had a similar problem, too: I wanted numbers and null on an input field that is not required. Worked through a number of different variations. I finally settled on this one, which seems to do the trick. You place a Directive, ntvFormValidity, on any form control that has native invalidity and that doesn't swizzle that invalid state into ng-invalid.

Sample use: <input type="number" formControlName="num" placeholder="0" ntvFormValidity>

Directive definition:

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);
}
}

The challenge was to get the ElementRef from the FormControl so that I could examine it. I know there's @ViewChild, but I didn't want to have to annotate each numeric input field with an ID and pass it to something else. So, I built a Directive which can ask for the ElementRef.

On Safari, for the HTML example above, Angular marks the form control invalid on inputs like "abc".

I think if I were to do this over, I'd probably build my own CVA for numeric input fields as that would provide even more control and make for a simple html.

Something like this:

<my-input-number formControlName="num" placeholder="0">

PS: If there's a better way to grab the FormControl for the directive, I'm guessing with Dependency Injection and providers on the declaration, please let me know so I can update my Directive (and this answer).

Using directive it becomes easy and can be used throughout the application

HTML

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

As .keyCode() and .which() are deprecated, codes are checked using .key() Referred from

Directive:

@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();
}
}
}

Try to put a minimum input and allow only numbers from 0 to 9. This worked for me in Angular Cli

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

Simplest and most effective way to do number validation is (it will restrict space and special character also)

if you dont want length restriction you can remove maxlength property

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
}

IMO the most robust and general way to do this is by checking if the value may be converted to number. For that add a validator:

numberValidator(control: FormControl) {
if (isNaN(control?.value)) {
return {
number: true
}
}
return null;
}


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


constructor(
private fb: FormBuilder,
) {
this.form = fb.group({
number: ['', [numberValidator]]
})
}
}

You can combine it with Validators.min and/or Validators.max to further limiting the accepted values.