108 votes

Angular2 - Champ de saisie n'acceptant que les nombres

En Angular 2, comment puis-je masquer un champ de saisie (textbox) de façon à ce 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 indiquer 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 chiffres ?

Quelles sont les différentes façons d'y parvenir ?

Note : J'ai besoin de réaliser ceci en utilisant seulement une zone de texte et non pas en utilisant le type de nombre d'entrée.

1 votes

Pourriez-vous simplement utiliser l'attribut html ? type=number

0 votes

@inoabrian Je veux réaliser ceci sans utiliser le type de numéro.

0 votes

Cela peut vous aider : stackoverflow.com/questions/39799436/

126voto

omeralper Points 4392

Vous pouvez utiliser les 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 comme un attribut

<input OnlyNumber="true" />

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

En utilisant une expression rationnelle, vous aurez 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 votes

C'est génial. Est-il possible de faire la même chose en utilisant les modèles RegEx ?

4 votes

Il ne permet pas le copier-coller.

0 votes

@Shardul juste ajouter (e.keyCode == 86 && e.ctrlKey === true) aux conditions, le copier fonctionne mais le coller ne fonctionnait pas

86voto

rashidnk Points 1523

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;

  }
}

41 votes

Le problème avec cette approche est que les événements clés ne permettent pas de capturer un utilisateur qui colle ou un navigateur qui remplit automatiquement le champ de saisie. Il s'agit donc d'une mauvaise solution.

0 votes

Excellente solution - merci

38voto

Elvis Fernandez Points 131

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

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

Utilisation :

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

0 votes

J'ai modifié la dernière ligne de la méthode validatevalue pour empêcher l'ajout d'un zéro en cas de pâte invalide. if (valid) { this.hostElement.nativeElement['value'] = value;}

0 votes

Pouvez-vous également ajouter la validation par glisser-déposer ? De plus, j'ai remarqué que la valeur des champs de saisie est modifiée par la valeur 0 padded pour le séparateur décimal de tête et de queue mais la valeur n'est pas mise à jour dans la variable de liaison bidirectionnelle. par exemple : [(NgModel)]="maVariable" , ici, si nous tapons .3 dans le champ de saisie, la valeur dans la saisie de texte change en 0.3 sur le flou mais la valeur dans maVariable reste toujours '.3' .

0 votes

Les entrées Supprimer et Entrer sont manquantes, mais la solution est très bonne.

33voto

JeanPaul A. Points 1605

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

Ce que je propose est une version simplifiée et mise à jour avec les derniers standards du web. Il est important de noter que event.keycode a été retiré des standards du web, et que les futures mises à jour des navigateurs pourraient ne plus le supporter. Voir https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode

En outre, la méthode

String.fromCharCode(e.keyCode);

ne garantit pas que le keyCode correspondant à la touche pressée par l'utilisateur correspond à la lettre attendue telle qu'elle est identifiée sur le clavier de l'utilisateur, puisque des configurations de clavier différentes donneront lieu à un keycode particulier pour des caractères différents. L'utilisation de cette méthode introduira des bogues difficiles à identifier, et peut facilement briser 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'ai exclu les fonctions couper, copier et coller, car elles ouvrent la porte à des bogues, notamment lorsque des personnes collent du texte indésirable dans les champs associés. Cela nécessiterait un processus de nettoyage sur un gestionnaire de touches, ce qui n'est pas le sujet de ce fil de discussion.

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

0 votes

C'est une approche vraiment intéressante. Avez-vous des suggestions sur la manière d'implémenter la fonctionnalité copier/coller sans avoir recours aux anciennes méthodes telles que (e.keyCode == 67 && e.ctrlKey === true) ?

1 votes

Je n'ai pas essayé personnellement, mais vous pouvez également écouter les événements de copier/coller qui sont déclenchés. Ils génèrent un ClipboardEvent ( developer.mozilla.org/fr/US/docs/Web/API/ClipboardEvent ) qui contient les données qui sont copiées/collées. Le seul inconvénient est que cette fonction est encore expérimentale et n'est prise en charge que par les navigateurs les plus récents. caniuse.com/#search=paste

0 votes

J'ai essayé une approche similaire mais malheureusement cela ne fonctionne pas pour tous les cas. Votre variable "next" suppose que le caractère pressé va à la fin de la valeur actuellement tapée. Ce n'est pas toujours le cas. Par exemple, si quelqu'un tape 100, et décide ensuite de le rendre 1100 en ajoutant un 1 devant. Votre variable "suivante" sera incorrecte (1001).

19voto

ketan pradhan Points 280
<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();
    }
  }

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X