Тип входного номера "только числовое значение"

Как я могу проверить правильность ввода type="number" только если значение является числовым или нулевым, используя только Reactive Forms (без директив)?
Только цифры [0-9] и. не допускаются, "е" или любые другие символы.


Что я пробовал до сих пор:

Шаблон:

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

Plunker

Проблема в том, что когда пользователь вводит что-то, что не является числом ("123e" или "abc"), значение FormControl становится null, имейте в виду, я не хочу, чтобы поле было обязательным, поэтому, если поле действительно является пустым null значением должно быть в силе.

Кросс-браузерная поддержка также важна (поля ввода чисел Chrome не позволяют пользователю вводить буквы - кроме "e", но FireFox и Safari делают).

Ответ 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 вы можете добавить шаблон Validators - "^ [0-9] * $"

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

Ответ 2

Самый простой способ - использовать библиотеку, подобную этой 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;   
      } 
    }

Ответ 3

Вам нужно использовать регулярные выражения в своем настраиваемом валидаторе. Например, здесь код, который позволяет вводить только 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

Ответ 4

Иногда проще просто попробовать что-то простое.

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)])

Связывание (this) необходимо, если вы помещаете валидатор в компонент, который, как я это делаю.

Ответ 5

У меня тоже была похожая проблема: я хотел числа и ноль в поле ввода, которое не требуется. Проработал ряд различных вариаций. Я наконец остановился на этом, который, кажется, делает свое дело. Вы помещаете директиву ntvFormValidity в любой элемент управления формы, который имеет собственную недействительность и не превращает это недопустимое состояние в ng-invalid.

Пример использования: <input type="number" formControlName="num" placeholder="0" ntvFormValidity>

Определение директивы:

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

Задача состояла в том, чтобы получить ElementRef из FormControl, чтобы я мог его изучить. Я знаю там @ViewChild, но я не хотел аннотировать каждое числовое поле ввода идентификатором и передавать его чему-то другому. Итак, я построил директиву, которая может запросить ElementRef.

В Safari для приведенного выше примера HTML Angular помечает элемент управления формы как недопустимый для таких вводов, как "abc".

Я думаю, что если бы я сделал это снова, я бы, вероятно, создал свой собственный CVA для числовых полей ввода, поскольку это обеспечило бы еще больший контроль и сделал бы простой HTML.

Что-то вроде этого:

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

PS: Если есть лучший способ получить FormControl для директивы, я полагаю, с помощью Dependency Injection и providers в декларации, пожалуйста, дайте мне знать, чтобы я мог обновить свою директиву (и этот ответ).