Angular2 - Campo di input per accettare solo numeri


86

In Angular 2, come posso mascherare un campo di input (casella di testo) in modo che accetti solo numeri e non caratteri alfabetici?

Ho il seguente input HTML:

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

L'input di cui sopra è un input di testo generico che può essere utilizzato come un semplice campo di testo o come un campo numerico, ad esempio, per mostrare l'anno.

Utilizzando Angular 2, come posso utilizzare lo stesso controllo di input e applicare una sorta di filtro / maschera su questo campo, in modo tale che accetti solo numeri?

Quali sono i diversi modi in cui posso ottenere questo risultato?

Nota: devo ottenere questo risultato utilizzando solo la casella di testo e non utilizzando il tipo di numero di input.


1
Saresti in grado di utilizzare solo l'attributo html? tipo = numero
inoabriano

@inoabrian Voglio raggiungere questo obiettivo senza utilizzare il tipo di numero.
Aniruddha Pondhe

Risposte:


112

Puoi usare direttive 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 devi scrivere il nome della direttiva nel tuo input come un attributo

<input OnlyNumber="true" />

non dimenticare di scrivere la tua direttiva nell'array delle dichiarazioni del tuo modulo.

Utilizzando regex avresti comunque bisogno di chiavi funzionali

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
È fantastico. In qualche modo posso ottenere lo stesso risultato utilizzando i modelli RegEx?
Aniruddha Pondhe

3
Non consente il copia-incolla.
Shardul

@Shardul aggiungi solo (e.keyCode == 86 && e.ctrlKey === true)alle condizioni, la copia funziona ma la pasta non funzionava
Al-Mothafar

Ho aggiunto e.keyCode == 109 || e.keyCode ==190per .e -ma -non viene accettato?
daniel

1
Come aggiungo spazio, più e meno?
Zahidul Islam Ruhel

65

Se non vuoi una direttiva

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

in component.html

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

in 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
Il problema con questo approccio è che gli eventi chiave non catturano un utente che incolla o un browser che riempie automaticamente il campo di input. Quindi questa è una cattiva soluzione.
Darryn Hosking

30

So che questa è una vecchia domanda, ma poiché questa è una funzionalità comune, voglio condividere le modifiche che ho fatto:

  • Separatore decimale personalizzato (punto o virgola)
  • Supporto solo per numeri interi o numeri interi e decimali
  • Supporto solo per numeri positivi o positivi e negativi
  • Convalida il segno meno (-) all'inizio
  • Supporto per incollare il mouse (con alcune limitazioni anche se https://caniuse.com/#feat=clipboard )
  • Supporto per il tasto di comando Mac
  • Sostituisci stringhe come ".33" e "33". per le versioni corrette: 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);
                }
            }
        }
    }
    

Utilizzo:

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

Ho cambiato l'ultima riga del metodo validatevalue per evitare di aggiungere uno zero per incolla non valida. if (valido) {this.hostElement.nativeElement ['value'] = value;}
Abdul Rehman detto il

puoi per favore aggiungere anche la convalida drag and drop? Inoltre, ho notato che il valore dei campi di input cambia nel valore 0 riempito per il separatore decimale iniziale e finale, ma il valore non si aggiorna nella variabile di associazione a due vie. per esempio: [(NgModel)] = "myVariable", qui, se digitiamo .3 nel campo di input, il valore nell'inserimento di testo cambia in 0.3 su blur ma il valore in myVariable rimane ancora '.3'.
Sushmit Sagar

Manca l'input di cancellazione e invio, ma comunque la soluzione è molto buona
Oleg Bondarenko

29

Vorrei basarmi sulla risposta data da @omeralper, che a mio parere ha fornito una buona base per una solida soluzione.

Quello che sto proponendo è una versione semplificata e aggiornata con i più recenti standard web. È importante notare che event.keycode viene rimosso dagli standard web e che i futuri aggiornamenti del browser potrebbero non supportarlo più. Vedi https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode

Inoltre, il metodo

String.fromCharCode(e.keyCode);

non garantisce che il keyCode relativo al tasto premuto dall'utente corrisponda alla lettera prevista come identificata sulla tastiera dell'utente, poiché diverse configurazioni di tastiera risulteranno in un particolare keycode caratteri diversi. L'utilizzo di questo introdurrà bug che sono difficili da identificare e possono facilmente interrompere la funzionalità per alcuni utenti. Piuttosto sto proponendo l'uso di event.key, vedere la documentazione qui https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key

Inoltre, vogliamo solo che l'output risultante sia un decimale valido. Ciò significa che i numeri 1, 11.2, 5000.2341234 dovrebbero essere accettati, ma il valore 1.1.2 non dovrebbe essere accettato.

Nota che nella mia soluzione sto escludendo la funzionalità taglia, copia e incolla poiché apre finestre per bug, specialmente quando le persone incollano testo indesiderato nei campi associati. Ciò richiederebbe un processo di pulizia su un gestore di keyup; che non è lo scopo di questo thread.

Ecco la soluzione che sto proponendo.

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

Questo è un approccio davvero interessante. Hai qualche suggerimento su come implementare la funzionalità copia / incolla senza ricorrere a metodi meno recenti come (e.keyCode == 67 && e.ctrlKey === true) ??
Ender2050

1
Non l'ho provato personalmente, tuttavia puoi ascoltare allo stesso modo gli eventi di copia / incolla che vengono attivati. Generano un ClipboardEvent ( developer.mozilla.org/en-US/docs/Web/API/ClipboardEvent ) che contiene i dati che vengono copiati / incollati. L'unico inconveniente è che questo è ancora sperimentale e supportato solo dai browser più recenti - caniuse.com/#search=paste
JeanPaul A.

Ho provato un approccio simile ma purtroppo non funziona per tutti i casi. La variabile "successiva" presume che il carattere premuto si trovi alla fine del valore attualmente digitato. Non è sempre così. Ad esempio, se qualcuno digita 100 e poi decide di renderlo 1100 aggiungendo un 1 in primo piano. La tua variabile "successiva" non sarà corretta (1001).
Carlos Rodriguez

Poiché il valore "successivo" viene utilizzato solo per verificare se l'importo di input è un decimale valido (e non per impostare il valore), aggiungerlo alla fine non cambierebbe la convalida dell'espressione regolare.
JeanPaul A.

Solo vorrei aggiungere questa riga da applicare nel controllo di input. <input myNumberOnly type = "text" id = "yourId">
Lrodriguez84

17

Una soluzione più concisa. Prova questa direttiva.

Può essere utilizzato anche se stai 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, ''));
  }
}

Usalo sui tuoi input in questo modo:

<input matInput formControlName="aNumberField" numberOnly>

1
Sebbene questa soluzione funzioni, innesca due volte gli eventi di modifica del modello, che ha detto che l'approccio all'uso di regex è quello giusto, ecco una versione che NON attiva
ntziolis

Al commento di ntziolis: Finora la soluzione di Ben Gulapa sta funzionando per me. Ma la soluzione a cui fa riferimento ntziolis non lo è. Perdonami se sbaglio, ma sembra che il problema con il codice al link sopra di stackblitz, almeno per me, fosse che l'ultimo carattere indesiderato che ho digitato, sebbene non fosse mai mostrato nell'interfaccia utente, in qualche modo ottenuto messo nella variabile vincolata del mio componente. Solo l'ultimo personaggio indesiderato.
user2367418

Per continuare il mio commento: utilizzo di Angular 7 e un testo di input HMTL limitato a due caratteri.
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

Devi usare type = "number" al posto del testo. È inoltre possibile specificare numeri massimi e minimi

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

2
Voglio ottenere questo risultato senza utilizzare il tipo di numero.
Aniruddha Pondhe

3
Il supporto per il tipo di numero è ancora abbastanza difettoso come descritto in questa risposta: stackoverflow.com/a/14995890/1156185
Nicolas Forney

9
Lo svantaggio di type="number"è che accetta il carattere ecome parte della notazione scientifica
user776686

12

puoi ottenerlo in questo modo

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

spero che questo ti possa aiutare.


potresti approfondire questo? cosa fa event.charCode == 8?
bosari

9

Potresti usare regex:

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

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

1
sì, è utile ma voglio (.) decimale anche nel mio campo di immissione
rinku Choudhary

6

Usa l' patternattributo per l'input come di seguito:

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

non funziona. quando inizi a digitare, digiti caratteri che sono sbagliati
Seyed-Amir-Mehrizi

6

So che ha molte risposte, ma avevo bisogno di gestire quanto segue (che nessuna delle risposte sembrava supportare completamente):

  • Supporto di textarea con opzione per multi-righe
  • Decimali o numeri negativi
  • Lunghezza massima per riga
  • Supporto cross-browser (Chrome, Edge, IE 11)
  • Gestione di operazioni ed eventi taglia / incolla

La soluzione mi permette di definire una textarea come questa:

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

O se voglio solo numeri interi positivi

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

Ecco la mia direttiva:

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

Per fare ciò, ho associato una funzione al metodo onInput in questo modo:

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

Ecco l'esempio all'interno del mio modulo:

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

Quindi ho aggiunto la seguente funzione al mio componente:

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

Questa regex /[^0-9]/gcerca tutto ciò che non è un numero e utilizzando l' .replaceho impostato per essere sostituito da niente. Quindi, quando un utente tenta di digitare un carattere che non è un numero (in questo caso un carattere che non va da zero a nove), sembra che nella casella di testo non accada nulla.


4

Bene, grazie a JeanPaul A. e rdanielmurphy. Avevo creato la mia direttiva personalizzata per limitare il campo di input solo al numero. Aggiunti anche gli attributi di input max e min. Funzionerà anche nell'angolo 7.

Angolare

    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

Un approccio moderno per la migliore risposta (senza e.keyCode deprecato):

@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
Questo e spettacolare! @Directive ({selector: "[inputNumericInput]"}) export class NumericInputDirective {@HostListener ()}
Nate

1
Funziona bene. Unico effetto collaterale osservato nel copia incolla. Consente il copia-incolla di stringhe non numeriche esterne. Ho cercato su Google e ho trovato una soluzione migliore che affronta questo @ stackblitz.com/edit/…
vinsinraw

4

Direttiva RegExp arbitraria

Ecco una piccola direttiva che usa regexp arbitraria e blocca l'utente per digitare un valore non valido

Per mascherare solo i numeri utilizzare

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

Sfortunatamente puoi ingannare questa soluzione inviando spam in circonferenza + tutto ciò che vuoi scrivere.
ProgFroz

3

Basta creare una direttiva e aggiungere sotto hostlistener:

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

Sostituisci il testo non valido con vuoto. Tutti i tasti e le combinazioni di tasti ora funzioneranno su tutti i browser fino a IE9.


Se il tipo di inizio con char, char non verrà aggiunto ma il conteggio della lunghezza del modello richiede 1. Come risolverlo ?. Inoltre, se l'elemento ha una lunghezza massima, quindi copia e incolla il contenuto misto, il conteggio del modello sarà la lunghezza massima. Ad esempio, la lunghezza massima ha 10, quindi copia e incolla 1238261jhgjh12987 nell'input aggiungerà solo 123816 ma la lunghezza del modello richiederebbe 10. Qualche soluzione?
Satheesh Natarajan

3

Modello per il modello di numero di cellulare valido ('^ ((\ + 91 -?) | 0)? [0-9] {10} $')

Modello per accettare solo il numero dal modello della casella di testo ("[0-9] *")

picchiettio per accettare solo il numero con un numero specifico, ad esempio: codice PIN. modello ('^ [0-9] {5} $')


2

Ho apportato alcune modifiche alla direttiva di cui sopra e implementato 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();
    }

  }
}

come dare il valore di lunghezza massima dal campo di input
Jason Brody

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

1

dalla risposta di @omeralper. Cambio un po 'che non accetterà il periodo ASCII (codice 110,190). e usa let ch = (e.key); per confrontare con l'espressione regolare quando cambi la lingua (come la lingua tailandese o giapponese) non accetta il carattere di quella lingua

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

spero che questo aiuto :)


1

Puoi creare questo Validator e importarlo nel tuo componente.
Fondamentalmente convalida la stringa di input del modulo:

  • controlla che non ci siano punti
  • converte la stringa in numero
  • check è un numero intero
  • il controllo è maggiore di zero

Per implementarlo nel tuo progetto:

  1. percorso suggerito nella cartella dell'app: src / app / validators / number.validator.ts
  2. importa nel tuo componente

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

  3. aggiungerlo al controllo del modulo
    inputNumber: ['', [NumberValidator.isInteger]],
  4. se non vuoi mostrare il carattere non valido, associa (change)="deleteCharIfInvalid()"a all'input, se form.get('inputNumber').hasError('isInteger')è true, cancella l'ultimo carattere inserito.
// 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;
    }        
}

Non Number.isInteger(Math.floor(control.value))sarà sempre vero? Penso che dovrebbe essere parseFloatinvece.
AndyTheEntity

1

Con il supporto per la sanificazione del contenuto incollato:

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

Eccone uno facile: Semplice direttiva sull'evento keydown controlla che la lunghezza di una chiave sia uno e la chiave non è un numero per preventDefault()e non renderà quel carattere.

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 />

Limitazioni: consentirà di incollare utilizzando un mouse in questo modo accetterà altri caratteri. Per evitare ciò è possibile passare il modello come input alla direttiva engOnChage quel modello cambia il valore solo ai numeri:

Come di seguito:

MODIFICA: codice aggiunto per rilevare il cambiamento nel modello e aggiornare il valore dell'input

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 il tipo di inizio con char, char non verrà aggiunto ma il conteggio della lunghezza del modello richiede 1. Come risolverlo?
Satheesh Natarajan

quando si controlla la lunghezza, rimane 0 nella direttiva prima e dopo aver apportato le modifiche. Se a un certo punto è uno, dovrebbe tornare rapidamente a 0.
Lahar Shah

No non lo è. Prova a legare numModel.length nel modello e controlla il conteggio della lunghezza
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();
          }
        };

      }
    }

La direttiva è anche il modo migliore per farlo


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

oppure: 2. nel file HTML:

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

nel file ts:

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

Questa 2 soluzione funziona


Utilizza i blocchi di codice per formattare gli snippet di codice.
YuS

0

fromCharCode restituisce "a" quando si preme sul tastierino numerico "1", quindi questo methoid dovrebbe essere evitato

(admin: non posso commentare come al solito)


0

Ho visto molti commenti sulla gestione di copia / incolla.

Per tornare indietro dalla risposta @omeralper, puoi aggiungere un gestore di eventi incolla alla direttiva onlyNumber per gestire il copia / incolla:

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

Ciò consentirà di copiare e incollare il contenuto nella casella di testo SOLO se si tratta di un numero. Questa è la soluzione più semplice. Cambiare il contenuto degli appunti per rimuovere i non numerici è molto più complicato e potrebbe non valerne la pena.

Per ottenere testo incollato da IE puoi usare quanto segue:

window.clipboardData.getData('Text');


0

Non sarebbe abbastanza semplice scrivere

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

}


0

È inoltre possibile creare una direttiva che implementa l'interfaccia ControlValueAccessor ( https://angular.io/api/forms/ControlValueAccessor ).

Vedi un esempio funzionante qui: https://stackblitz.com/edit/angular-input-field-to-accept-only-numbers

Puoi ascoltare l'evento 'input' e non è necessario controllare i codici chiave. Supporta copia e incolla e si integra perfettamente con l'API Angular Forms grazie all'interfaccia ControlValueAccessor.

Direttiva:

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

Utilizzo:

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

0

Casting perché funziona anche con lo 0 iniziale come 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, ''));
  }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.