Angular2 - Campo de entrada para aceitar apenas números


86

No Angular 2, como posso mascarar um campo de entrada (caixa de texto) de forma que ele aceite apenas números e não caracteres alfabéticos?

Eu tenho a seguinte entrada de HTML:

<input 
  type="text" 
  *ngSwitchDefault 
  class="form-control" 
  (change)="onInputChange()" 
  [(ngModel)]="config.Value" 
  (focus)="handleFocus($event)" 
  (blur)="handleBlur($event)"
/>

A entrada acima é uma entrada de texto genérica que pode ser usada como um campo de texto simples ou como um campo numérico, por exemplo, para mostrar o ano.

Usando o Angular 2, como posso usar o mesmo controle de entrada e aplicar algum tipo de filtro / máscara neste campo, de forma que aceite apenas números?

Quais são as diferentes maneiras de fazer isso?

Observação: preciso fazer isso usando apenas a caixa de texto e não o tipo de número de entrada.


1
Você seria capaz de usar apenas o atributo html? type = number
inoabrian

@inoabrian Quero fazer isso sem usar o tipo de número.
Aniruddha Pondhe

Respostas:


112

Você pode usar as diretivas angular2. Plunkr

import { Directive, ElementRef, HostListener, Input } from '@angular/core';

@Directive({
  selector: '[OnlyNumber]'
})
export class OnlyNumber {

  constructor(private el: ElementRef) { }

  @Input() OnlyNumber: boolean;

  @HostListener('keydown', ['$event']) onKeyDown(event) {
    let e = <KeyboardEvent> event;
    if (this.OnlyNumber) {
      if ([46, 8, 9, 27, 13, 110, 190].indexOf(e.keyCode) !== -1 ||
        // Allow: Ctrl+A
        (e.keyCode === 65 && (e.ctrlKey || e.metaKey)) ||
        // Allow: Ctrl+C
        (e.keyCode === 67 && (e.ctrlKey || e.metaKey)) ||
        // Allow: Ctrl+V
        (e.keyCode === 86 && (e.ctrlKey || e.metaKey)) ||
        // Allow: Ctrl+X
        (e.keyCode === 88 && (e.ctrlKey || e.metaKey)) ||
        // Allow: home, end, left, right
        (e.keyCode >= 35 && e.keyCode <= 39)) {
          // let it happen, don't do anything
          return;
        }
        // Ensure that it is a number and stop the keypress
        if ((e.shiftKey || (e.keyCode < 48 || e.keyCode > 57)) && (e.keyCode < 96 || e.keyCode > 105)) {
            e.preventDefault();
        }
      }
  }
}

e você precisa escrever o nome da diretiva em sua entrada como um atributo

<input OnlyNumber="true" />

não se esqueça de escrever sua diretiva no array de declarações de seu módulo.

Usando regex, você ainda precisa de chaves funcionais

export class OnlyNumber {

  regexStr = '^[0-9]*$';
  constructor(private el: ElementRef) { }

  @Input() OnlyNumber: boolean;

  @HostListener('keydown', ['$event']) onKeyDown(event) {
    let e = <KeyboardEvent> event;
    if (this.OnlyNumber) {
        if ([46, 8, 9, 27, 13, 110, 190].indexOf(e.keyCode) !== -1 ||
        // Allow: Ctrl+A
        (e.keyCode == 65 && e.ctrlKey === true) ||
        // Allow: Ctrl+C
        (e.keyCode == 67 && e.ctrlKey === true) ||
        // Allow: Ctrl+V
        (e.keyCode == 86 && e.ctrlKey === true) ||
        // Allow: Ctrl+X
        (e.keyCode == 88 && e.ctrlKey === true) ||
        // Allow: home, end, left, right
        (e.keyCode >= 35 && e.keyCode <= 39)) {
          // let it happen, don't do anything
          return;
        }
      let ch = String.fromCharCode(e.keyCode);
      let regEx =  new RegExp(this.regexStr);    
      if(regEx.test(ch))
        return;
      else
         e.preventDefault();
      }
  }
}

1
Isso é ótimo. Posso conseguir o mesmo usando padrões RegEx?
Aniruddha Pondhe

3
Não permite copiar e colar.
Shardul de

@Shardul basta adicionar (e.keyCode == 86 && e.ctrlKey === true)às condições, copiar está funcionando, mas colar não funcionou
Al-Mothafar

Eu adicionei e.keyCode == 109 || e.keyCode ==190para .e -mas -não é aceito?
daniel

1
Como adiciono espaço, mais e menos?
Zahidul Islam Ruhel

65

Se você não quer uma diretiva

https://stackblitz.com/edit/numeric-only

em component.html

<input (keypress)="numberOnly($event)" type="text">

em component.ts

export class AppComponent {

  numberOnly(event): boolean {
    const charCode = (event.which) ? event.which : event.keyCode;
    if (charCode > 31 && (charCode < 48 || charCode > 57)) {
      return false;
    }
    return true;

  }
}

33
O problema com essa abordagem é que os eventos principais não capturam a colagem do usuário ou o preenchimento automático do campo de entrada pelo navegador. Portanto, esta é uma solução ruim.
Darryn Hosking de

30

Eu sei que esta é uma questão antiga, mas como esta é uma funcionalidade comum, quero compartilhar as modificações que fiz:

  • Separador decimal personalizado (ponto ou vírgula)
  • Suporte apenas para números inteiros ou números inteiros e decimais
  • Suporte apenas para números positivos ou positivos e negativos
  • Verifique se o sinal de menos (-) está no início
  • Suporte para colagem do mouse (com algumas limitações, embora https://caniuse.com/#feat=clipboard )
  • Suporte para tecla de comando do Mac
  • Substitua strings como ".33" e "33." para as versões corretas: 0,33 e 33,0

    import { Directive, ElementRef, HostListener, Input } from '@angular/core';
    
    @Directive({ selector: '[NumbersOnly]' })
    export class NumbersOnly { 
    
        @Input() allowDecimals: boolean = true;
        @Input() allowSign: boolean = false;
        @Input() decimalSeparator: string = '.';
    
        previousValue: string = '';
    
        // --------------------------------------
        //  Regular expressions
        integerUnsigned: string = '^[0-9]*$';
        integerSigned: string = '^-?[0-9]+$';
        decimalUnsigned: string = '^[0-9]+(.[0-9]+)?$';
        decimalSigned: string = '^-?[0-9]+(.[0-9]+)?$';
    
        /**
         * Class constructor
         * @param hostElement
         */
        constructor(private hostElement: ElementRef) { }
    
        /**
         * Event handler for host's change event
         * @param e
         */
        @HostListener('change', ['$event']) onChange(e) {
    
                this.validateValue(this.hostElement.nativeElement.value);
    }
    
    /**
     * Event handler for host's paste event
     * @param e
     */
    @HostListener('paste', ['$event']) onPaste(e) {
    
        // get and validate data from clipboard
        let value = e.clipboardData.getData('text/plain');
        this.validateValue(value);
        e.preventDefault();
    }
    
    /**
     * Event handler for host's keydown event
     * @param event
     */
    @HostListener('keydown', ['$event']) onKeyDown(e: KeyboardEvent) {
    
        let cursorPosition: number = e.target['selectionStart'];
        let originalValue: string = e.target['value'];
        let key: string = this.getName(e);
        let controlOrCommand = (e.ctrlKey === true || e.metaKey === true);
        let signExists = originalValue.includes('-');
        let separatorExists = originalValue.includes(this.decimalSeparator);
    
        // allowed keys apart from numeric characters
        let allowedKeys = [
            'Backspace', 'ArrowLeft', 'ArrowRight', 'Escape', 'Tab'
        ];
    
        // when decimals are allowed, add
        // decimal separator to allowed codes when
        // its position is not close to the the sign (-. and .-)
        let separatorIsCloseToSign = (signExists && cursorPosition <= 1);
        if (this.allowDecimals && !separatorIsCloseToSign && !separatorExists) {
    
            if (this.decimalSeparator == '.')
                allowedKeys.push('.');
            else
                allowedKeys.push(',');
        }
    
        // when minus sign is allowed, add its
        // key to allowed key only when the
        // cursor is in the first position, and
        // first character is different from
        // decimal separator
        let firstCharacterIsSeparator = (originalValue.charAt(0) != this.decimalSeparator);
        if (this.allowSign && !signExists &&
            firstCharacterIsSeparator && cursorPosition == 0) {
    
            allowedKeys.push('-');
        }
    
        // allow some non-numeric characters
        if (allowedKeys.indexOf(key) != -1 ||
            // Allow: Ctrl+A and Command+A
            (key == 'a' && controlOrCommand) ||
            // Allow: Ctrl+C and Command+C
            (key == 'c' && controlOrCommand) ||
            // Allow: Ctrl+V and Command+V
            (key == 'v' && controlOrCommand) ||
            // Allow: Ctrl+X and Command+X
            (key == 'x' && controlOrCommand)) {
            // let it happen, don't do anything
            return;
        }
    
        // save value before keydown event
        this.previousValue = originalValue;
    
        // allow number characters only
        let isNumber = (new RegExp(this.integerUnsigned)).test(key);
        if (isNumber) return; else e.preventDefault();
    }
    
    /**
     * Test whether value is a valid number or not
     * @param value
     */
    validateValue(value: string): void {
    
        // choose the appropiate regular expression
        let regex: string;
        if (!this.allowDecimals && !this.allowSign) regex = this.integerUnsigned;
        if (!this.allowDecimals && this.allowSign) regex = this.integerSigned;
        if (this.allowDecimals && !this.allowSign) regex = this.decimalUnsigned;
        if (this.allowDecimals &&  this.allowSign) regex = this.decimalSigned;
    
        // when a numbers begins with a decimal separator,
        // fix it adding a zero in the beginning
        let firstCharacter = value.charAt(0);
        if (firstCharacter == this.decimalSeparator)
            value = 0 + value;
    
        // when a numbers ends with a decimal separator,
        // fix it adding a zero in the end
        let lastCharacter = value.charAt(value.length-1);
        if (lastCharacter == this.decimalSeparator)
            value = value + 0;
    
        // test number with regular expression, when
        // number is invalid, replace it with a zero
        let valid: boolean = (new RegExp(regex)).test(value);
        this.hostElement.nativeElement['value'] = valid ? value : 0;
    }
    
    /**
     * Get key's name
     * @param e
     */
    getName(e): string {
    
        if (e.key) {
    
            return e.key;
    
        } else {
    
            // for old browsers
            if (e.keyCode && String.fromCharCode) {
    
                switch (e.keyCode) {
                    case   8: return 'Backspace';
                    case   9: return 'Tab';
                    case  27: return 'Escape';
                    case  37: return 'ArrowLeft';
                    case  39: return 'ArrowRight';
                    case 188: return ',';
                    case 190: return '.';
                    case 109: return '-'; // minus in numbpad
                    case 173: return '-'; // minus in alphabet keyboard in firefox
                    case 189: return '-'; // minus in alphabet keyboard in chrome
                    default: return String.fromCharCode(e.keyCode);
                }
            }
        }
    }
    

Uso:

 <input NumbersOnly
        [allowDecimals]="true"
        [allowSign]="true"
        type="text">

Mudei a última linha do método validatevalue para evitar adicionar um zero para colar inválido. if (válido) {this.hostElement.nativeElement ['value'] = value;}
Abdul Rehman disse em

você também pode adicionar validação de arrastar e soltar? Além disso, percebi que o valor dos campos de entrada muda para o valor 0 preenchido para o separador decimal à esquerda e à direita, mas o valor não é atualizado na variável de ligação bidirecional. por exemplo: [(NgModel)] = "myVariable", aqui, se digitarmos .3 no campo de entrada, o valor na entrada de texto muda para 0,3 no desfoque, mas o valor em myVariable ainda permanece '.3'.
Sushmit Sagar

Falta a entrada Delete e Enter, mas mesmo assim a solução é muito boa
Oleg Bondarenko,

29

Gostaria de desenvolver a resposta dada por @omeralper, que, em minha opinião, forneceu uma boa base para uma solução sólida.

O que estou propondo é uma versão simplificada e atualizada com os padrões da web mais recentes. É importante observar que event.keycode foi removido dos padrões da web e as atualizações futuras do navegador podem não suportá-lo mais. Consulte https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode

Além disso, o método

String.fromCharCode(e.keyCode);

não garante que o keyCode pertencente à tecla que está sendo pressionada pelo usuário mapeie para a letra esperada conforme identificada no teclado do usuário, uma vez que diferentes configurações de teclado resultarão em um determinado código de tecla em caracteres diferentes. Usar isso irá introduzir bugs que são difíceis de identificar e podem facilmente quebrar a funcionalidade para certos usuários. Em vez disso, estou propondo o uso de event.key, consulte os documentos aqui https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key

Além disso, queremos apenas que a saída resultante seja um decimal válido. Isso significa que os números 1, 11.2, 5000.2341234 devem ser aceitos, mas o valor 1.1.2 não deve ser aceito.

Observe que, na minha solução, estou excluindo a funcionalidade de recortar, copiar e colar, uma vez que abre janelas para erros, especialmente quando as pessoas colam texto indesejado em campos associados. Isso exigiria um processo de limpeza em um manipulador de teclas; que não é o escopo deste tópico.

Aqui está a solução que estou propondo.

import { Directive, ElementRef, HostListener } from '@angular/core';

@Directive({
    selector: '[myNumberOnly]'
})
export class NumberOnlyDirective {
    // Allow decimal numbers. The \. is only allowed once to occur
    private regex: RegExp = new RegExp(/^[0-9]+(\.[0-9]*){0,1}$/g);

    // Allow key codes for special events. Reflect :
    // Backspace, tab, end, home
    private specialKeys: Array<string> = [ 'Backspace', 'Tab', 'End', 'Home' ];

    constructor(private el: ElementRef) {
    }

    @HostListener('keydown', [ '$event' ])
    onKeyDown(event: KeyboardEvent) {
        // Allow Backspace, tab, end, and home keys
        if (this.specialKeys.indexOf(event.key) !== -1) {
            return;
        }

        // Do not use event.keycode this is deprecated.
        // See: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode
        let current: string = this.el.nativeElement.value;
        // We need this because the current value on the DOM element
        // is not yet updated with the value from this event
        let next: string = current.concat(event.key);
        if (next && !String(next).match(this.regex)) {
            event.preventDefault();
        }
    }
}

Esta é uma abordagem realmente interessante. Você tem alguma sugestão sobre como implementar a funcionalidade copiar / colar sem recorrer a métodos mais antigos, como (e.keyCode == 67 && e.ctrlKey === true) ??
Ender2050

1
Eu não tentei fazer isso pessoalmente, no entanto, você pode ouvir da mesma forma os eventos copiar / colar que são acionados. Eles geram um ClipboardEvent ( developer.mozilla.org/en-US/docs/Web/API/ClipboardEvent ) que contém os dados que estão sendo copiados / colados. A única desvantagem é que isso ainda é experimental e compatível apenas com os navegadores mais recentes - caniuse.com/#search=paste
A.

Tentei uma abordagem semelhante, mas infelizmente isso não funciona para todos os casos. Sua "próxima" variável assume que o caractere pressionado está no final do valor digitado no momento. Nem sempre é o caso. Por exemplo, se alguém digitar 100 e, em seguida, decidir torná-lo 1100 acrescentando 1 à frente. Sua "próxima" variável estará incorreta (1001).
Carlos Rodriguez

Como o valor 'próximo' é usado apenas para verificar se a quantidade de entrada é um decimal válido (e não para definir o valor), anexá-lo no final não mudaria a validação do regex.
JeanPaul A.

Só eu gostaria de adicionar esta linha para aplicar no controle de entrada. <input myNumberOnly type = "text" id = "yourId">
Lrodriguez84

17

Uma solução mais concisa. Experimente esta diretiva.

Também pode ser usado se você estiver usando ReactiveForms.

export class NumberOnlyDirective {
  private el: NgControl;

  constructor(private ngControl: NgControl) {
    this.el = ngControl;
  }

  // Listen for the input event to also handle copy and paste.
  @HostListener('input', ['$event.target.value'])
  onInput(value: string) {
    // Use NgControl patchValue to prevent the issue on validation
    this.el.control.patchValue(value.replace(/[^0-9]/g, ''));
  }
}

Use-o em suas entradas como este:

<input matInput formControlName="aNumberField" numberOnly>

1
Embora esta solução funcione, ela dispara eventos de mudança de modelo duas vezes, dito que a abordagem de usar regex é a correta, aqui está uma versão que NÃO dispara eventos alterados de modelo duas vezes: stackblitz.com/edit/…
ntziolis

Ao comentário de ntziolis: Até agora, a solução de Ben Gulapa está funcionando para mim. Mas a solução referenciada por ntziolis não é. Perdoe-me se estou errado, mas parece que o problema com o código no link acima para stackblitz, pelo menos para mim, foi que o último caractere indesejado que digitei, embora não tenha aparecido nunca na interface do usuário, de alguma forma colocado na variável associada do meu componente. Apenas o último personagem indesejado.
user2367418

Para continuar meu comentário: Usando Angular 7 e um texto de entrada HMTL limitado a dois caracteres.
user2367418

15
<input type="text" (keypress)="keyPress($event)">


  keyPress(event: any) {
    const pattern = /[0-9\+\-\ ]/;

    let inputChar = String.fromCharCode(event.charCode);
    if (event.keyCode != 8 && !pattern.test(inputChar)) {
      event.preventDefault();
    }
  }

14

Você precisa usar type = "número" em vez de texto. Você também pode especificar os números máximo e mínimo

<input type="number" name="quantity" min="1" max="5">

2
Quero conseguir isso sem usar o tipo de número.
Aniruddha Pondhe

3
O suporte para o tipo de número ainda tem muitos bugs, conforme descrito nesta resposta: stackoverflow.com/a/14995890/1156185
Nicolas Forney

9
A desvantagem type="number"é que ele aceita caracteres ecomo parte da notação científica
user776686

12

você pode conseguir assim

<input type="text" pInputText (keypress)="onlyNumberKey($event)" maxlength="3"> 

onlyNumberKey(event) {
    return (event.charCode == 8 || event.charCode == 0) ? null : event.charCode >= 48 && event.charCode <= 57;
}

//for Decimal you can use this as

onlyDecimalNumberKey(event) {
    let charCode = (event.which) ? event.which : event.keyCode;
    if (charCode != 46 && charCode > 31
        && (charCode < 48 || charCode > 57))
        return false;
    return true;
}

espero que isso ajude você.


você poderia elaborar sobre isso? o que event.charCode == 8 está fazendo?
bosari

9

Você pode usar regex:

<input type="text" (keypress)="numericOnly($event)">

numericOnly(event): boolean {    
    let patt = /^([0-9])$/;
    let result = patt.test(event.key);
    return result;
}

1
sim, é útil, mas eu quero (.) decimal também no meu campo de entrada
rinku Choudhary

6

Use o patternatributo para entrada como abaixo:

<input type="text" pattern="[0-9]+" >

Não está funcionando. quando você começa a digitar, você digita caracteres que estão errados
Seyed-Amir-Mehrizi,

6

Eu sei que isso tem muitas respostas, mas eu precisava lidar com o seguinte (que nenhuma das respostas parecia apoiar totalmente):

  • Suporte de textarea com opção para multi-linhas
  • Decimais ou números negativos
  • Comprimento máximo por linha
  • Suporte para vários navegadores (Chrome, Edge, IE 11)
  • Lidar com operações e eventos de cortar / colar

A solução me permite definir uma área de texto como esta:

<textarea class="form-control" [(ngModel)]="this.myModelVariable"
    appOnlyNumbers [allowNegative]="true" [allowMultiLine]="true" 
    [allowDecimal]="true" [maxLength]="10"
    placeholder="Enter values (one per line)"></textarea>

Ou se eu só quero números inteiros positivos

<textarea class="form-control" [(ngModel)]="this.myModelVariable"
    appOnlyNumbers [allowMultiLine]="true" [maxLength]="9"
    placeholder="Enter values (one per line)"></textarea>

Aqui está minha diretriz:

import { Directive, HostListener, Input, ElementRef } from '@angular/core';

@Directive({
  selector: '[appOnlyNumbers]'
})
export class OnlyNumbersDirective {
  constructor(private el: ElementRef) { }

  @Input() allowMultiLine: boolean = false;
  @Input() allowNegative: boolean = false;
  @Input() allowDecimal: boolean = false;
  @Input() maxLength: number = 0;
  regex: RegExp;

  @HostListener('keypress', ['$event'])
  onKeyPress(event: KeyboardEvent) {
    this.validate(event, event.key === 'Enter' ? '\n' : event.key);
  }

  @HostListener('paste', ['$event'])
  onPaste(event: Event) {
    const pastedText = (<any>window).clipboardData && (<any>window).clipboardData.getData('Text') // If IE, use window
      || <ClipboardEvent>event && (<ClipboardEvent>event).clipboardData.getData('text/plain'); // Non-IE browsers
    this.validate(event, pastedText);
  }

  @HostListener('cut', ['$event'])
  onCut(event: Event) {
    this.validate(event, '');
  }

  validate(event: Event, text: string) {
    const txtInput = this.el.nativeElement;
    const newValue = (txtInput.value.substring(0, txtInput.selectionStart)
      + text + txtInput.value.substring(txtInput.selectionEnd));
    if (!this.regex) {
      this.regex = <RegExp>eval('/^'
        + (this.allowNegative ? '-?' : '')
        + (this.allowDecimal ? '((\\d+\\.?)|(\\.?))\\d*' : '\\d*')
        + '$/g');
    }
    var lines = this.allowMultiLine ? newValue.split('\n') : [newValue];
    for (let line of lines) {
      let lineText = line.replace('\r', '');
      if (this.maxLength && lineText.length > this.maxLength || !lineText.match(this.regex)) {
        event.preventDefault();
        return;
      }
    }
  }

}

4

Para fazer isso, vinculei uma função ao método onInput como este:

(input)="stripText(infoForm.get('uin'))

Aqui está o exemplo dentro do meu formulário:

<form [formGroup]="infoForm" (submit)="next()" class="ui form">
    <input type="text" formControlName="uin" name="uin" id="uin" (input)="stripText(infoForm.get('uin'))" required/>
</form>

Em seguida, adicionei a seguinte função ao meu componente:

  stripText(control: FormControl) {
   control.setValue(control.value.replace(/[^0-9]/g, ''));
  }

Este regex /[^0-9]/gprocura por qualquer coisa que não seja um número e usando .replaceeu o defini para ser substituído por nada. Portanto, quando um usuário tenta digitar um caractere que não é um número (neste caso, um caractere que não é de zero a nove), parece que nada acontece na caixa de texto.


4

Bem, obrigado a JeanPaul A. e rdanielmurphy. Eu fiz minha própria diretiva personalizada para limitar o campo de entrada a apenas um número. Também foram adicionados os atributos de entrada máximo e mínimo. Funcionará no angular 7 também.

Angular

    import { Directive, ElementRef, Input, HostListener } from '@angular/core';

@Directive({
  selector: '[appNumberOnly]'
})
export class NumberOnlyDirective {
  // Allow decimal numbers. The \. is only allowed once to occur
  private regex: RegExp = new RegExp(/^[0-9]+(\.[0-9]*){0,1}$/g);

  // Allow key codes for special events. Reflect :
  // Backspace, tab, end, home
  private specialKeys: Array<string> = ['Backspace', 'Tab', 'End', 'Home'];
  constructor(private el: ElementRef) { }

  @Input() maxlength: number;
  @Input() min: number;
  @Input() max: number;

  @HostListener('keydown', ['$event'])
  onKeyDown(event: KeyboardEvent) {
    // Allow Backspace, tab, end, and home keys
    if (this.specialKeys.indexOf(event.key) !== -1) {
      return;
    }

    // Do not use event.keycode this is deprecated.
    // See: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode
    const current: string = this.el.nativeElement.value;

    // We need this because the current value on the DOM element
    // is not yet updated with the value from this event
    const next: string = current.concat(event.key);
    if (next && !String(next).match(this.regex) || (this.maxlength && next.length > this.maxlength) ||
      (this.min && +next < this.min) ||
      (this.max && +next >= this.max)) {
      event.preventDefault();
    }
  }

  @HostListener('paste', ['$event']) onPaste(event) {
    // Don't allow pasted text that contains non-numerics
    const pastedText = (event.originalEvent || event).clipboardData.getData('text/plain');

    if (pastedText) {
      const regEx = new RegExp('^[0-9]*$');
      if (!regEx.test(pastedText) || (this.maxlength && pastedText.length > this.maxlength) ||
        (this.min && +pastedText < this.min) ||
        (this.max && +pastedText >= this.max)) {
        event.preventDefault();
      }
    }
  }

}

HTML

<input type="text" class="text-area" [(ngModel)]="itemName" maxlength="3" appNumberOnly />

4

Uma abordagem moderna para a melhor resposta (sem e.keyCode obsoleto):

@HostListener('keydown', ['$event']) onKeyDown(event) {
    let e = <KeyboardEvent> event;
    if (['Delete', 'Backspace', 'Tab', 'Escape', 'Enter', 'NumLock', 'ArrowLeft', 'ArrowRight', 'End', 'Home', '.'].indexOf(e.key) !== -1 ||
      // Allow: Ctrl+A
      (e.key === 'a' && (e.ctrlKey || e.metaKey)) ||
      // Allow: Ctrl+C
      (e.key === 'c' && (e.ctrlKey || e.metaKey)) ||
      // Allow: Ctrl+V
      (e.key === 'v' && (e.ctrlKey || e.metaKey)) ||
      // Allow: Ctrl+X
      (e.key === 'x' && (e.ctrlKey || e.metaKey))) {
      // let it happen, don't do anything
      return;
    }
    // Ensure that it is a number and stop the keypress
    if ((e.shiftKey || ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'].indexOf(e.key) === -1)) {
      e.preventDefault();
    }
}

1
Isso é incrível! @Directive ({selector: "[inputNumericInput]"}) export class NumericInputDirective {@HostListener ()}
Nate

1
Funciona bem. Único efeito colateral observado no copy paste. Permite copiar e colar de strings externas não numéricas. Pesquisei e encontrei uma solução melhor que aborda isso @ stackblitz.com/edit/…
vinsinraw

4

Diretiva Arbitrary RegExp

Aqui está uma pequena diretiva que usa regexp arbitrária e bloqueia o usuário para digitar um valor inválido

Para mascarar apenas números, use

<input [allowedRegExp]="'^[0-9]*$'" type="text" ... >

Infelizmente, você pode enganar essa solução enviando spam para circunflexo + o que quer que você queira escrever.
ProgFroz,

3

Basta criar uma diretiva e adicionar hostlistener abaixo:

@HostListener('input', ['$event'])
    onInput(event: Event) {
        this.elementRef.nativeElement.value = (<HTMLInputElement>event.currentTarget).value.replace(/[^0-9]/g, '');
    }

Substitua o texto inválido por vazio. Todas as teclas e combinações de teclas agora funcionarão em todos os navegadores até o IE9.


Se iniciar digite com char, char não acrescentará, mas a contagem do comprimento do modelo leva 1. Como resolver isso ?. Além disso, se o elemento tiver um comprimento máximo, copie e cole o conteúdo misto, a contagem do modelo será o comprimento máximo. Por exemplo, o comprimento máximo tem 10, então copie e cole 1238261jhgjh12987 na entrada irá anexar 123816 apenas, mas o comprimento do modelo levaria 10. Alguma solução?
Satheesh Natarajan

3

Padrão para o padrão de número de celular válido ('^ ((\ + 91 -?) | 0)? [0-9] {10} $')

Padrão para aceitar apenas o número do padrão da caixa de texto ('[0-9] *')

padrão para aceitar apenas o número com um número específico, por exemplo: Pincode. padrão ('^ [0-9] {5} $')


2

Fiz algumas modificações na diretiva acima e implementei min, max, maxlength.

   import { Directive, ElementRef, HostListener, Input } from '@angular/core';

@Directive({
  selector: '[numberOnly]'
})
export class NumbersOnlyDirective {

  private regex: RegExp = new RegExp(/[0-9]/g);
  // Allow key codes for special events. Reflect :
  private specialKeys: Array<number> = [46, 8, 9, 27, 13, 110, 190, 35, 36, 37, 39];
  // Backspace, tab, end, home

  @Input() maxlength: number;
  @Input() min: number;
  @Input() max: number;

  constructor(private el: ElementRef) {
  }
    @HostListener('keydown', ['$event'])
    onKeyDown(event: KeyboardEvent) {
    e = <KeyboardEvent>event;

if ((
  (this.specialKeys.indexOf(event.which) > -1) ||
  // to allow backspace, enter, escape, arrows  
  (e.which == 65 && e.ctrlKey == true) ||
  // Allow: Ctrl+C        
  (e.which == 67 && e.ctrlKey == true) ||
  // Allow: Ctrl+X
  (e.which == 88 && e.ctrlKey == true))) {
  return;
} else if (// to allow numbers  
  (e.which >= 48 && e.which <= 57) ||
  // to allow numpad number  
  (event.which >= 96 && event.which <= 105)) { }
else {
      event.preventDefault();
    }
    let current: string = this.el.nativeElement.value;

    let next: string = current.concat(event.key);
    if ((next && !String(next).match(this.regex)) ||
      (this.maxlength && next.length > this.maxlength) ||
      (this.min && +next < this.min) ||
      (this.max && +next >= this.max)) {
      event.preventDefault();
    }

  }
}

como fornecer o valor do comprimento máximo do campo de entrada
Jason Brody

<input id = "COMN" class = "wb-e-inp-1__input" type = "text" appNumberOnly maxlength = "10" /> trabalhando
Jason Brody

1

da resposta de @omeralper. Mudo um pouco que não aceita ponto ascii (código 110,190). e use let ch = (e.key); para comparar com a expressão regular quando você altera o idioma (como o tailandês ou japonês), ele não aceitará o caractere desse idioma

export class OnlyNumber {

  regexStr = '^[0-9]*$';
  constructor(private el: ElementRef) { }

  @Input() OnlyNumber: boolean;

  @HostListener('keydown', ['$event']) onKeyDown(event) {
    let e = <KeyboardEvent> event;
    if (this.OnlyNumber) {
      // console.log(event, this.OnlyNumber);
        if ([46, 8, 9, 27, 13].indexOf(e.keyCode) !== -1) {
          return;
        }
      let ch = (e.key);
      let regEx =  new RegExp(this.regexStr);   
      if(regEx.test(ch))
        return;
      else
         e.preventDefault();
    }
  }
}

espero que ajude :)


1

Você pode criar este Validador e importá-lo em seu componente.
Valida basicamente a string de entrada do formulário:

  • verifique se não há ponto
  • converte string em número
  • cheque é um inteiro
  • cheque é maior que zero

Para implementá-lo em seu projeto:

  1. caminho sugerido na pasta do seu aplicativo: src / app / validators / number.validator.ts
  2. importar em seu componente

    import { NumberValidator } from '../../validators/number.validator';

  3. adicione-o ao controle do formulário
    inputNumber: ['', [NumberValidator.isInteger]],
  4. se você não quiser mostrar o char inválido, ligue (change)="deleteCharIfInvalid()"a à entrada, se form.get('inputNumber').hasError('isInteger')for true, delete o último char inserido.
// FILE: src/app/validators/number.validator.ts

import { FormControl } from '@angular/forms';

export interface ValidationResult {
    [key: string]: boolean;
}

export class NumberValidator {

    public static isInteger(control: FormControl): ValidationResult {
        // check if string has a dot
        let hasDot:boolean = control.value.indexOf('.') >= 0 ? true : false;
        // convert string to number
        let number:number = Math.floor(control.value);
        // get result of isInteger()
        let integer:boolean = Number.isInteger(number);
        // validate conditions 
        let valid:boolean = !hasDot && integer && number>0;
        console.log('isInteger > valid', hasDot, number, valid);
        if (!valid) {
            return { isInteger: true };
        }
        return null;
    }        
}

Não Number.isInteger(Math.floor(control.value))será sempre verdade? Eu acho que deveria ser em parseFloatvez disso.
AndyTheEntity

1

Com suporte para higienização de conteúdo colado:

import { Directive, ElementRef, HostListener, Input } from '@angular/core';

@Directive({
  selector: '[NumbersOnly]'
})
export class NumbersOnlyDirective {

    DIGITS_REGEXP =  new RegExp(/\D/g);
    constructor(private el: ElementRef) { 

        // Sanatize clipboard by removing any non-numeric input after pasting
        this.el.nativeElement.onpaste = (e:any) => {
            e.preventDefault();
            let text;
            let clp = (e.originalEvent || e).clipboardData;
            if (clp === undefined || clp === null) {
                text = (<any>window).clipboardData.getData('text') || '';
                if (text !== '') {
                    text = text.replace(this.DIGITS_REGEXP, '');
                    if (window.getSelection) {
                        let newNode = document.createElement('span');
                        newNode.innerHTML = text;
                        window.getSelection().getRangeAt(0).insertNode(newNode);
                    } else {
                        (<any>window).selection.createRange().pasteHTML(text);
                    }
                }
            } else {
                text = clp.getData('text/plain') || '';
                if (text !== '') {
                    text = text.replace(this.DIGITS_REGEXP, '');
                    document.execCommand('insertText', false, text);
                }
            }
        };
    }

  @HostListener('keydown', ['$event']) onKeyDown(event) {
    let e = <KeyboardEvent> event;
    if ([46, 8, 9, 27, 13, 110, 190].indexOf(e.keyCode) !== -1 ||
      // Allow: Ctrl+A
      (e.keyCode === 65 && (e.ctrlKey || e.metaKey)) ||
      // Allow: Ctrl+C
      (e.keyCode === 67 && (e.ctrlKey || e.metaKey)) ||
      // Allow: Ctrl+V
      (e.keyCode === 86 && (e.ctrlKey || e.metaKey)) ||
      // Allow: Ctrl+X
      (e.keyCode === 88 && (e.ctrlKey || e.metaKey)) ||
      // Allow: home, end, left, right
      (e.keyCode >= 35 && e.keyCode <= 39)) {
        // let it happen, don't do anything
        return;
      }
      // Ensure that it is a number and stop the keypress
      if ((e.shiftKey || (e.keyCode < 48 || e.keyCode > 57)) && (e.keyCode < 96 || e.keyCode > 105)) {
          e.preventDefault();
      }
    }

}

1

Aqui é fácil: Diretriz simples No evento keydown, ela verifica se o comprimento de uma chave é um e a chave não é um número para preventDefault()e não renderiza aquele caractere.

import {Directive, ElementRef, HostListener} from '@angular/core';

@Directive({
    selector: '[numbersOnly]'
})
export class NumbersOnlyDirective {
    @HostListener('keydown', ['$event'])
    keyDownEvent(event: KeyboardEvent) {
        if (event.key.length === 1 && (event.which < 48 || event.which > 57)) {
            event.preventDefault();
        }
    }

}

HTML:

<input type="text" [(ngModel)]="numModel" numbersOnly />

Limitações: Permitirá colar com o mouse dessa forma aceitará outros caracteres. Para evitar isso, você pode passar o modelo como entrada para a diretiva engOnChage para esse modelo alterar o valor apenas para números:

Como abaixo:

EDIT: Adicionado código para detectar mudança no modelo e atualizar o valor da entrada

import {Directive, ElementRef, HostListener, Input, OnChanges} from '@angular/core';

@Directive({
    selector: '[numbersOnly]'
})
export class NumbersOnlyDirective implements OnChanges {

    @Input() numbersOnly: any;

    constructor(private el: ElementRef) {}

    @HostListener('keydown', ['$event'])
    keyDownEvent(event: KeyboardEvent) {
        // Add other conditions if need to allow ctr+c || ctr+v
        if (event.key.length === 1 && (event.which < 48 || event.which > 57)) {
            event.preventDefault();
        }
    }

    ngOnChanges(changes) {
        if (changes.numbersOnly) {
            this.el.nativeElement.value = this.el.nativeElement.value.replace(/[^0-9]/g, '');
        }
    }

}

HTML:

<input type="text" [(ngModel)]="numModel" [numbersOnly]="numModel" />

Se iniciar digite com char, char não acrescentará, mas a contagem do comprimento do modelo leva 1. Como resolver isso?
Satheesh Natarajan

quando você verifica o comprimento, ele permanece 0 na diretiva antes e depois de fazer alterações. Se em algum momento for um, deve voltar a 0 rapidamente.
Lahar Shah

Não, não é. Apenas tente ligar numModel.length no modelo e verifique a contagem de comprimento
Satheesh Natarajan

1
 import {Directive, ElementRef, HostListener, Output, EventEmitter} from '@angular/core';


    //only-digits
    @Directive({
      selector: '[only-digits]'
    })
    export class OnlyDigits {

      constructor(public el: ElementRef) {

        this.el.nativeElement.onkeypress = (evt) => {
          if (evt.which < 48 || evt.which > 57) {
            evt.preventDefault();
          }
        };

      }
    }

A diretiva também é a melhor maneira de fazer isso


1
  1. <input oninput="this.value=this.value.replace(/[^0-9]/g,'')"

ou: 2. no arquivo HTML:

 <input [(ngModel)]="data" (keypress)="stripText($event)"
     class="form-control">

no arquivo ts:

stripText(event) {
const seperator  = '^([0-9])';
const maskSeperator =  new RegExp(seperator , 'g');  
let result =maskSeperator.test(event.key);   return result;   }

Esta 2 solução funciona


Use blocos de código para formatar seus snippets de código.
YuS

0

fromCharCode retorna 'a' ao pressionar o teclado numérico '1', então este metóide deve ser evitado

(admin: não poderia comentar como de costume)


0

Eu vi muitos comentários sobre como copiar / colar.

Para pegar carona na resposta @omeralper, você pode adicionar um manipulador de eventos paste à diretiva onlyNumber para lidar com copiar / colar:

 @HostListener('paste', ['$event']) onPaste(event) {
  // Don't allow pasted text that contains non-numerics
  var pastedText = (event.originalEvent || event).clipboardData.getData('text/plain');

  if (pastedText) {
    var regEx = new RegExp('^[0-9]*$');
    if (!regEx.test(pastedText)) {
      event.preventDefault();
    }
}

Isso só permitirá que o conteúdo seja copiado e colado na caixa de texto SOMENTE se for um número. Essa é a solução mais simples. Alterar o conteúdo da área de transferência para remover números não numéricos é muito mais complicado e pode não valer a pena.

Para obter texto colado do IE, você pode usar o seguinte:

window.clipboardData.getData('Text');


0

Não seria simples o suficiente apenas escrever

onlyNumbers(event) {
if(isNaN(event.target.value * 1)) {
 console.log("Not a number")
} else {
  console.log("Number")
}

}


0

Você também pode criar uma diretiva que implemente a interface ControlValueAccessor ( https://angular.io/api/forms/ControlValueAccessor ).

Veja o exemplo de trabalho aqui: https://stackblitz.com/edit/angular-input-field-to-accept-only-numbers

Você pode ouvir o evento de 'entrada' e não há necessidade de verificar os códigos de tecla. Ele suporta copiar e colar e se integra perfeitamente com a API Angular Forms devido à Interface ControlValueAccessor.

Diretriz:

@Directive({
    ...
    selector: '[onlyNumber]'
})
export class OnlyNumberDirective implements ControlValueAccessor {
private onChange: (val: string) => void;
...
private value: string;

constructor(
    private elementRef: ElementRef,
    private renderer: Renderer2
) {
}

...

@HostListener('input', ['$event.target.value'])
onInputChange(value: string) {
    const filteredValue: string = filterValue(value);
    this.updateTextInput(filteredValue, this.value !== filteredValue);
}

private updateTextInput(value, propagateChange) {
    this.renderer.setProperty(this.elementRef.nativeElement, 'value', value);
    if (propagateChange) {
        this.onChange(value);
    }
    this.value = value;
}

// ControlValueAccessor Interface
...

registerOnChange(fn: any): void {
    this.onChange = fn;
}

writeValue(value: string): void {
    value = value ? String(value) : '';
    this.updateTextInput(value, false);
}
}


function filterValue(value): string {
    return value.replace(/[^0-9]*/g, '');
}

Uso:

<input name="number" type="text" onlyNumber [(ngModel)]="someNumber">

0

Fundição porque funciona também com 0 líder, como 00345

@Directive({
  selector: '[appOnlyDigits]'
})
export class AppOnlyDigitsDirective {
  @HostListener('input', ['$event'])
  onKeyDown(ev: KeyboardEvent) {
    const input = ev.target as HTMLInputElement;
    input.value = String(input.value.replace(/\D+/g, ''));
  }
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.