Angular2 - Champ de saisie pour n'accepter que des nombres


86

Dans Angular 2, comment puis-je masquer un champ de saisie (zone de texte) de sorte qu'il n'accepte que des chiffres et non des caractères alphabétiques?

J'ai l'entrée HTML suivante:

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

L'entrée ci-dessus est une entrée de texte générique qui peut être utilisée soit comme un simple champ de texte, soit comme un champ numérique, par exemple, pour afficher l'année.

En utilisant Angular 2, comment puis-je utiliser le même contrôle d'entrée et appliquer une sorte de filtre / masque sur ce champ, de sorte qu'il n'accepte que des nombres?

Quelles sont les différentes manières d'y parvenir?

Remarque: je dois y parvenir en utilisant uniquement une zone de texte et sans utiliser le type de numéro d'entrée.


1
Seriez-vous capable d'utiliser simplement l'attribut html? type = number
inoabrian

@inoabrian Je veux y parvenir sans utiliser le type de nombre.
Aniruddha Pondhe

Réponses:


112

Vous pouvez utiliser des directives 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();
        }
      }
  }
}

et vous devez écrire le nom de la directive dans votre entrée en tant qu'attribut

<input OnlyNumber="true" />

n'oubliez pas d'écrire votre directive dans le tableau des déclarations de votre module.

En utilisant regex, vous auriez toujours besoin de clés fonctionnelles

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
C'est génial. Est-ce que je peux obtenir la même chose en utilisant des modèles RegEx?
Aniruddha Pondhe

3
Il n'autorise pas le copier-coller.
Shardul

@Shardul juste ajouter (e.keyCode == 86 && e.ctrlKey === true)aux conditions, la copie fonctionne mais la pâte ne fonctionnait pas
Al-Mothafar

J'ai ajouté e.keyCode == 109 || e.keyCode ==190pour .et -mais -n'est pas accepté?
daniel

1
Comment ajouter de l'espace, plus et moins?
Zahidul Islam Ruhel

65

Si vous ne voulez pas de directive

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

dans component.html

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

dans 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
Le problème avec cette approche est que les événements clés ne capturent pas un collage d'utilisateur ou un navigateur remplissant automatiquement le champ de saisie. C'est donc une mauvaise solution.
Darryn Hosking

30

Je sais que c'est une vieille question, mais comme il s'agit d'une fonctionnalité commune, je souhaite partager les modifications que j'ai apportées:

  • Séparateur décimal personnalisé (point ou virgule)
  • Prise en charge des entiers uniquement ou des nombres entiers et décimaux
  • Prise en charge des nombres positifs uniquement ou des positifs et des négatifs
  • Valider le signe moins (-) est au début
  • Prise en charge du collage de la souris (avec quelques limitations via https://caniuse.com/#feat=clipboard )
  • Prise en charge de la touche de commande Mac
  • Remplacez les chaînes telles que ".33" et "33". pour les versions correctes: 0.33 et 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);
                }
            }
        }
    }
    

Usage:

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

J'ai changé la dernière ligne de la méthode validatevalue pour éviter d'ajouter un zéro pour une pâte invalide. if (valide) {this.hostElement.nativeElement ['value'] = value;}
Abdul Rehman a dit

pouvez-vous également ajouter une validation par glisser-déposer? En outre, j'ai remarqué que la valeur des champs d'entrée change en valeur 0 complétée pour le séparateur décimal de début et de fin, mais la valeur ne se met pas à jour dans la variable de liaison bidirectionnelle. par exemple: [(NgModel)] = "myVariable", ici, si nous saisissons .3 dans le champ de saisie, la valeur de la saisie de texte passe à 0,3 en cas de flou mais la valeur de myVariable reste toujours ".3".
Sushmit Sagar

L'entrée Supprimer et Entrer est manquante, mais de toute façon la solution est très bonne
Oleg Bondarenko

29

Je voudrais m'appuyer sur la réponse de @omeralper, qui à mon avis a fourni une bonne base pour une solution solide.

Ce que je propose, c'est une version simplifiée et à jour avec les derniers standards du web. Il est important de noter que event.keycode est supprimé des normes Web et que les futures mises à jour du navigateur pourraient ne plus le prendre en charge. Voir https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode

De plus, la méthode

String.fromCharCode(e.keyCode);

ne garantit pas que le keyCode se rapportant à la touche enfoncée par l'utilisateur correspond à la lettre attendue comme identifiée sur le clavier de l'utilisateur, car différentes configurations de clavier entraîneront un code de touche particulier de caractères différents. Son utilisation introduira des bogues difficiles à identifier et peut facilement interrompre la fonctionnalité pour certains utilisateurs. Je propose plutôt l'utilisation de event.key, voir la documentation ici https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key

De plus, nous voulons seulement que la sortie résultante soit une décimale valide. Cela signifie que les nombres 1, 11.2, 5000.2341234 doivent être acceptés, mais que la valeur 1.1.2 ne doit pas être acceptée.

Notez que dans ma solution, j'exclus la fonctionnalité couper, copier et coller car elle ouvre les fenêtres pour les bogues, en particulier lorsque les gens collent du texte indésirable dans les champs associés. Cela nécessiterait un processus de nettoyage sur un gestionnaire de keyup; ce qui n'est pas la portée de ce fil.

Voici la solution que je propose.

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

C'est une approche vraiment intéressante. Avez-vous des suggestions sur la façon d'implémenter la fonctionnalité de copier / coller sans recourir à des méthodes plus anciennes telles que (e.keyCode == 67 && e.ctrlKey === true) ??
Ender2050

1
Je n'ai pas personnellement essayé cela, mais vous pouvez également écouter les événements copier / coller qui sont déclenchés. Ils génèrent un ClipboardEvent ( developer.mozilla.org/en-US/docs/Web/API/ClipboardEvent ) qui contient les données copiées / collées. Le seul inconvénient est que cela est encore expérimental et pris en charge uniquement par les derniers navigateurs - caniuse.com/#search=paste
JeanPaul A.

J'ai essayé une approche similaire mais malheureusement cela ne fonctionne pas pour tous les cas. Votre variable «suivant» suppose que le caractère pressé se trouve à la fin de la valeur actuellement saisie. Ce n'est pas toujours le cas. Par exemple, si quelqu'un tape 100, puis décide de le rendre 1100 en ajoutant un 1 à l'avant. Votre variable «suivante» sera incorrecte (1001).
Carlos Rodriguez

Étant donné que la valeur 'next' n'est utilisée que pour vérifier si le montant d'entrée est une décimale valide (et non pour définir la valeur), l'ajouter à la fin ne changerait pas la validation de l'expression régulière.
JeanPaul A.

Seulement je voudrais ajouter cette ligne à appliquer dans le contrôle d'entrée. <input myNumberOnly type = "text" id = "yourId">
Lrodriguez84

17

Une solution plus concise. Essayez cette directive.

Peut également être utilisé si vous utilisez 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, ''));
  }
}

Utilisez-le sur vos entrées comme ceci:

<input matInput formControlName="aNumberField" numberOnly>

1
Bien que cette solution fonctionne, elle déclenche deux fois des événements de changement de modèle, cela dit que l'approche d'utilisation de regex est la bonne, voici une version qui ne déclenche PAS deux fois les événements de changement de modèle: stackblitz.com/edit/…
ntziolis

Au commentaire de ntziolis: Jusqu'à présent, la solution de Ben Gulapa fonctionne pour moi. Mais la solution référencée par ntziolis ne l'est pas. Pardonnez-moi si je me trompe, mais il semble que le problème avec le code du lien ci-dessus vers stackblitz, du moins pour moi, était que le dernier caractère indésirable que j'ai tapé, bien qu'il ne s'affiche jamais dans l'interface utilisateur, a en quelque sorte obtenu mis dans la variable liée de mon composant. Juste le dernier personnage indésirable.
user2367418 le

Pour continuer mon commentaire: Utilisation d'Angular 7 et d'un texte d'entrée HMTL limité à deux caractères.
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

Vous devez utiliser type = "number" à la place du texte. Vous pouvez également spécifier des nombres maximum et minimum

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

2
Je veux y parvenir sans utiliser le type de nombre.
Aniruddha Pondhe

3
La prise en charge du type de numéro est encore assez boguée comme décrit dans cette réponse: stackoverflow.com/a/14995890/1156185
Nicolas Forney

9
L'inconvénient de type="number" est qu'il accepte le caractère edans le cadre de la notation scientifique
user776686

12

tu peux l'atteindre comme ça

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

J'espère que cela vous aidera.


Pouvez-vous développer? que fait event.charCode == 8?
bosari

9

Vous pouvez utiliser regex:

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

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

1
oui c'est utile mais je veux (.) décimal aussi dans mon champ de saisie
rinku Choudhary

6

Utilisez l' patternattribut pour l'entrée comme ci-dessous:

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

ça ne marche pas. lorsque vous commencez à taper, vous tapez des caractères que c'est faux
Seyed-Amir-Mehrizi

6

Je sais que cela a beaucoup de réponses, mais je devais gérer les éléments suivants (qu'aucune des réponses ne semblait pleinement soutenir):

  • Prise en charge de textarea avec option pour multi-lignes
  • Décimales ou nombres négatifs
  • Longueur max par ligne
  • Prise en charge de plusieurs navigateurs (Chrome, Edge, IE 11)
  • Gestion des opérations et des événements de couper / coller

La solution me permet de définir une zone de texte comme celle-ci:

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

Ou si je veux juste des entiers positifs

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

Voici ma directive:

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

Pour ce faire, j'ai lié une fonction à la méthode onInput comme ceci:

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

Voici l'exemple dans mon formulaire:

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

Ensuite, j'ai ajouté la fonction suivante à mon composant:

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

Cette expression régulière /[^0-9]/grecherche tout ce qui n'est pas un nombre et en utilisant .replaceje l'ai défini pour qu'il soit remplacé par rien. Ainsi, lorsqu'un utilisateur tente de saisir un caractère qui n'est pas un nombre (dans ce cas, un caractère qui n'est pas de zéro à neuf), il semble que rien ne se passe dans la zone de texte.


4

Eh bien merci à JeanPaul A. et rdanielmurphy. J'avais créé ma propre directive personnalisée pour limiter le champ de saisie au nombre uniquement. Ajout des attributs d'entrée max et min. Fonctionnera également en angulaire 7.

Angulaire

    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

Une approche moderne pour la meilleure réponse (sans e.keyCode obsolète):

@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
C'est génial! @Directive ({selector: "[inputNumericInput]"}) classe d'exportation NumericInputDirective {@HostListener ()}
Nate

1
Fonctionne bien. Seul effet secondaire observé en copier-coller. Il permet le copier-coller de chaînes non numériques externes. Googlé et trouvé une meilleure solution qui résout ce @ stackblitz.com/edit/…
vinsinraw

4

Directive RegExp arbitraire

Voici une petite directive qui utilise une expression rationnelle arbitraire et bloque l'utilisateur pour taper une valeur invalide

Pour masquer uniquement les nombres, utilisez

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

Malheureusement, vous pouvez tromper cette solution en spammant circonflex + tout ce que vous voulez écrire.
ProgFroz

3

Créez simplement une directive et ajoutez ci-dessous hostlistener:

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

Remplacez le texte non valide par un texte vide. Toutes les touches et combinaisons de touches fonctionneront désormais sur tous les navigateurs jusqu'à IE9.


Si le type de début avec char, char ne s'ajoutera pas mais le compte de la longueur du modèle prend 1. Comment résoudre cela ?. De plus, si l'élément a une longueur maximale, copiez et collez le contenu mixte, le nombre de modèles serait la longueur maximale. Par exemple, la longueur maximale a 10, puis copier et coller 1238261jhgjh12987 à l'entrée ajoutera 123816 uniquement mais la longueur du modèle prendrait 10. Des solutions?
Satheesh Natarajan

3

Modèle pour le modèle de numéro de mobile valide ('^ ((\ + 91 -?) | 0)? [0-9] {10} $')

Modèle pour accepter uniquement le nombre du modèle de zone de texte ('[0-9] *')

patter pour accepter uniquement un nombre avec un numéro spécifique, par exemple: Pincode. motif ('^ [0-9] {5} $')


2

J'ai apporté quelques modifications à la directive ci-dessus et mis en œuvre 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();
    }

  }
}

comment donner la valeur de longueur maximale à partir du champ de saisie
Jason Brody

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

1

de la réponse de @omeralper. Je change un peu qui n'accepte pas la période ascii (code clé 110,190). et utilisez let ch = (e.key); pour comparer avec l'expression régulière lorsque vous changez de langue (comme le thaï ou le japonais), il n'acceptera pas le caractère de ces langues

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

espérons cette aide :)


1

Vous pouvez créer ce validateur et l'importer dans votre composant.
Valide essentiellement la chaîne d'entrée du formulaire:

  • vérifiez qu'il n'y a pas de point
  • convertit la chaîne en nombre
  • check est un entier
  • la vérification est supérieure à zéro

Pour l'implémenter dans votre projet:

  1. chemin suggéré dans le dossier de votre application: src / app / validators / number.validator.ts
  2. importer dans votre composant

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

  3. l'ajouter au contrôle de formulaire
    inputNumber: ['', [NumberValidator.isInteger]],
  4. si vous ne voulez pas afficher le caractère invalide, liez a (change)="deleteCharIfInvalid()"à l'entrée, si form.get('inputNumber').hasError('isInteger')c'est le cas true, supprimez le dernier caractère inséré.
// 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;
    }        
}

Ce ne Number.isInteger(Math.floor(control.value))sera pas toujours vrai? Je pense que ça devrait l'être à la parseFloatplace.
AndyTheEntity

1

Avec prise en charge de la désinfection du contenu collé:

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

En voici une simple: Directive simple Lors de l'événement keydown, il vérifie que la longueur d'une clé est un et que la clé n'est pas un nombre à preventDefault()et il ne rendra pas ce caractère.

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

Limitations: Cela permettra de coller à l'aide d'une souris de cette façon acceptera d'autres caractères. Pour éviter cela, vous pouvez passer le modèle en entrée de la directive etngOnChage à ce modèle, modifiez la valeur uniquement en nombres:

Comme ci-dessous:

EDIT: Ajout du code pour détecter les changements dans le modèle et mettre à jour la valeur de l'entrée

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

Si le type de début avec char, char ne s'ajoutera pas mais le compte de la longueur du modèle prend 1. Comment résoudre cela?
Satheesh Natarajan

quand vérifiez-vous la longueur, elle reste 0 dans la directive avant et après les modifications. Si à un moment donné c'est un, il devrait revenir rapidement à 0.
Lahar Shah

Non ce n'est pas. Essayez simplement de lier numModel.length dans le modèle et vérifiez le nombre de longueurs
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 directive est également le meilleur moyen de le faire


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

ou: 2. dans le fichier HTML:

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

dans le fichier ts:

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

Cette solution 2 fonctionne


Veuillez utiliser des blocs de code pour mettre en forme vos extraits de code.
YuS

0

fromCharCode renvoie 'a' en appuyant sur le pavé numérique '1' donc ce méthoïde doit être évité

(administrateur: n'a pas pu commenter comme d'habitude)


0

J'ai vu beaucoup de commentaires sur la gestion du copier / coller.

Pour récupérer la réponse @omeralper, vous pouvez ajouter un gestionnaire d'événements de collage à la directive onlyNumber pour gérer le copier / coller:

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

Cela permettra uniquement au contenu d'être copié et collé dans la zone de texte UNIQUEMENT s'il s'agit d'un nombre. C'est la solution la plus simple. Changer le contenu du presse-papiers pour supprimer les éléments non numériques est beaucoup plus compliqué et pourrait ne pas en valoir la peine.

Pour obtenir du texte collé à partir d'IE, vous pouvez utiliser ce qui suit:

window.clipboardData.getData('Text');


0

Ne serait pas assez simple pour écrire

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

}


0

Vous pouvez également créer une directive qui implémente l'interface ControlValueAccessor ( https://angular.io/api/forms/ControlValueAccessor ).

Voir l'exemple de travail ici: https://stackblitz.com/edit/angular-input-field-to-accept-only-numbers

Vous pouvez écouter l'événement 'input' et il n'est pas nécessaire de vérifier les codes clés. Il prend en charge le copier-coller et s'intègre parfaitement à l'API Angular Forms grâce à l'interface ControlValueAccessor.

Directif:

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

Usage:

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

0

Casting car il fonctionne également avec un 0 de tête comme 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, ''));
  }
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.