Angular2 - Pole wprowadzania do akceptowania tylko liczb


86

W Angular 2, jak mogę zamaskować pole wejściowe (pole tekstowe) tak, aby akceptowało tylko liczby, a nie znaki alfabetyczne?

Mam następujące dane wejściowe HTML:

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

Powyższe dane wejściowe to ogólne dane wejściowe, które mogą być używane jako proste pole tekstowe lub jako pole numeryczne, na przykład w celu wyświetlenia roku.

Używając Angulara 2, jak mogę użyć tej samej kontrolki wejściowej i zastosować jakiś rodzaj filtru / maski w tym polu, tak aby akceptował tylko liczby?

Jakie są różne sposoby, w jakie mogę to osiągnąć?

Uwaga: Muszę to osiągnąć używając tylko pola tekstowego i nie używając typu numeru wejściowego.


1
Czy byłbyś w stanie po prostu użyć atrybutu html? type = number
inoabrian

@inoabrian Chcę to osiągnąć bez używania typu liczbowego.
Aniruddha Pondhe,

Odpowiedzi:


112

Możesz użyć dyrektyw 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();
        }
      }
  }
}

i musisz wpisać nazwę dyrektywy w swoim wejściu jako atrybut

<input OnlyNumber="true" />

nie zapomnij napisać swojej dyrektywy w tablicy deklaracji swojego modułu.

Używając wyrażenia regularnego, nadal potrzebujesz klawiszy funkcyjnych

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
To wspaniale. Jakikolwiek sposób mogę osiągnąć to samo za pomocą wzorców RegEx?
Aniruddha Pondhe,

3
Nie zezwala na kopiowanie i wklejanie.
Shardul

@Shardul wystarczy dodać (e.keyCode == 86 && e.ctrlKey === true)do warunków, kopia działa, ale nie działa pasta
Al-Mothafar

1
Jak dodać spację, plus i minus?
Zahidul Islam Ruhel


65

Jeśli nie chcesz dyrektywy

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

w komponencie.html

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

w 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
Problem z tym podejściem polega na tym, że kluczowe zdarzenia nie przechwytują wklejenia użytkownika ani automatycznego wypełniania pola wejściowego przez przeglądarkę. Więc to jest kiepskie rozwiązanie.
Darryn Hosking

30

Wiem, że to stare pytanie, ale ponieważ jest to powszechna funkcja, chcę podzielić się modyfikacjami, które wprowadziłem:

  • Niestandardowy separator dziesiętny (kropka lub przecinek)
  • Obsługa tylko liczb całkowitych lub liczb całkowitych i dziesiętnych
  • Obsługa tylko liczb dodatnich lub pozytywów i negatywów
  • Potwierdź znak minus (-) znajduje się na początku
  • Obsługa wklejania myszy (z pewnymi ograniczeniami, chociaż https://caniuse.com/#feat=clipboard )
  • Obsługa klawisza poleceń Mac
  • Zastąp ciągi, takie jak „.33” i „33”. dla poprawnych wersji: 0.33 i 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);
                }
            }
        }
    }
    

Stosowanie:

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

Zmieniłem ostatni wiersz metody validatevalue, aby zapobiec dodaniu zera dla nieprawidłowego wklejania. if (valid) {this.hostElement.nativeElement ['value'] = value;}
Abdul Rehman Sayed

czy możesz również dodać walidację metodą przeciągnij i upuść? Zauważyłem również, że wartość pól wejściowych zmienia się na wartość 0 dla początkowego i końcowego separatora dziesiętnego, ale wartość nie jest aktualizowana w dwukierunkowej zmiennej wiążącej. na przykład: [(NgModel)] = "myVariable", tutaj, jeśli wpiszemy .3 w polu wejściowym, wartość w polu tekstowym zmieni się na 0,3 przy rozmyciu, ale wartość w myVariable nadal pozostanie '.3'.
Sushmit Sagar

Brakuje danych Delete and Enter, ale i tak rozwiązanie jest bardzo dobre
Oleg Bondarenko

29

Chciałbym oprzeć się na odpowiedzi udzielonej przez @omeralper, która moim zdaniem była dobrą podstawą do solidnego rozwiązania.

Proponuję uproszczoną i aktualną wersję z najnowszymi standardami sieciowymi. Należy zauważyć, że kod event.keycode został usunięty ze standardów sieciowych, a przyszłe aktualizacje przeglądarki mogą go już nie obsługiwać. Zobacz https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode

Ponadto metoda

String.fromCharCode(e.keyCode);

nie gwarantuje, że keyCode odnoszący się do klawisza wciskanego przez użytkownika jest odwzorowywany na oczekiwaną literę identyfikowaną na klawiaturze użytkownika, ponieważ różne konfiguracje klawiatury spowodują, że dany kod klawisza będzie miał różne znaki. Użycie tego spowoduje wprowadzenie błędów, które są trudne do zidentyfikowania i mogą łatwo uszkodzić funkcjonalność niektórych użytkowników. Raczej proponuję użycie event.key, zobacz dokumentację tutaj https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key

Ponadto chcemy, aby wynikowe dane wyjściowe były poprawnymi liczbami dziesiętnymi. Oznacza to, że należy zaakceptować liczby 1, 11.2, 5000.2341234, ale nie należy przyjąć wartości 1.1.2.

Zauważ, że w moim rozwiązaniu wykluczam funkcję wycinania, kopiowania i wklejania, ponieważ otwiera okna w poszukiwaniu błędów, zwłaszcza gdy ludzie wklejają niechciany tekst w powiązanych polach. To wymagałoby procesu czyszczenia w programie obsługi klucza; co nie jest przedmiotem tego wątku.

Oto rozwiązanie, które proponuję.

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

To naprawdę ciekawe podejście. Czy masz jakieś sugestie, jak zaimplementować funkcję kopiowania / wklejania bez uciekania się do starszych metod, takich jak (e.keyCode == 67 && e.ctrlKey === true)?
Ender2050

1
Osobiście tego nie próbowałem, jednak podobnie możesz słuchać wyzwalanych zdarzeń kopiowania / wklejania. Generują ClipboardEvent ( developer.mozilla.org/en-US/docs/Web/API/ClipboardEvent ), który zawiera dane, które są kopiowane / wklejane. Jedyną wadą jest to, że jest to nadal eksperymentalne i obsługiwane tylko przez najnowsze przeglądarki - caniuse.com/#search=paste
JeanPaul A.

Próbowałem podobnego podejścia, ale niestety nie działa to w każdym przypadku. Twoja zmienna „next” zakłada, że ​​wciśnięty znak znajduje się na końcu aktualnie wpisanej wartości. Nie zawsze tak jest. Na przykład, jeśli ktoś wpisze 100, a następnie zdecyduje się zrobić to jako 1100, dodając 1 na początku. Twoja „następna” zmienna będzie nieprawidłowa (1001).
Carlos Rodriguez

Ponieważ wartość „next” jest używana tylko do sprawdzenia, czy wprowadzona kwota jest prawidłową liczbą dziesiętną (a nie do ustawiania wartości), dołączenie jej na końcu nie zmieniłoby weryfikacji wyrażenia regularnego.
JeanPaul A.

Tylko chciałbym dodać tę linię do zastosowania w kontroli wejścia. <input myNumberOnly type = "text" id = "yourId">
Lrodriguez84

17

Bardziej zwięzłe rozwiązanie. Wypróbuj tę dyrektywę.

Może być również używany, jeśli używasz 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, ''));
  }
}

Użyj go na swoich wejściach w ten sposób:

<input matInput formControlName="aNumberField" numberOnly>

1
Chociaż to rozwiązanie działa, wywołuje zdarzenia zmiany modelu dwukrotnie, co oznacza, że ​​podejście do używania wyrażenia regularnego jest właściwe, tutaj jest wersja, która NIE uruchamia zdarzeń zmiany modelu dwukrotnie: stackblitz.com/edit/ ...
ntziolis

Do komentarza ntziolis: Jak dotąd rozwiązanie Bena Gulapy działa dla mnie. Ale rozwiązanie, do którego odwołuje się ntziolis, nie jest. Wybacz, jeśli się mylę, ale wygląda na to, że problem z kodem pod powyższym linkiem do stackblitz, przynajmniej dla mnie, polegał na tym, że ostatnia niechciana litera, którą wpisałem, chociaż nie pokazywała się nigdy w interfejsie użytkownika, jakoś się udało umieścić w zmiennej związanej mojego komponentu. Tylko ostatnia niechciana postać.
user2367418

Kontynuując mój komentarz: używając Angular 7 i tekstu wejściowego HMTL ograniczonego do dwóch znaków.
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();
    }
  }


12

możesz to osiągnąć w ten sposób

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

mam nadzieję, że to ci pomoże.


czy mógłbyś to rozwinąć? co robi event.charCode == 8?
bosari

9

Możesz użyć wyrażenia regularnego:

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

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

1
tak, to jest pomocne, ale chcę (.) dziesiętne również w moim polu wejściowym
rinku Choudhary

6

Użyj patternatrybutu do wprowadzania danych, jak poniżej:

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

to nie działa. kiedy zaczynasz pisać, wpisujesz znaki, które są złe
Seyed-Amir-Mehrizi

6

Wiem, że ma wiele odpowiedzi, ale musiałem poradzić sobie z następującymi (których żadna z odpowiedzi nie wydawała się w pełni wspierać):

  • Obsługa textarea z opcją dla wielu linii
  • Liczby dziesiętne lub ujemne
  • Maksymalna długość na linię
  • Obsługa wielu przeglądarek (Chrome, Edge, IE 11)
  • Obsługa operacji i zdarzeń wytnij / wklej

Rozwiązanie pozwala mi zdefiniować obszar tekstowy w następujący sposób:

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

Lub jeśli chcę tylko dodatnich liczb całkowitych

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

Oto moja dyrektywa:

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

Aby to osiągnąć, powiązałem funkcję z metodą onInput w następujący sposób:

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

Oto przykład w moim formularzu:

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

Następnie dodałem następującą funkcję do mojego komponentu:

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

To wyrażenie regularne /[^0-9]/gwyszukuje wszystko, co nie jest liczbą i używając .replaceustawiam go tak, aby był niczym. Więc kiedy użytkownik próbuje wpisać znak, który nie jest liczbą (w tym przypadku znak, który nie jest liczbą od zera do dziewiątki), wygląda to tak, jakby nic się nie działo w polu tekstowym.


4

Dzięki dla JeanPaul A. i rdanielmurphy. Zrobiłem własną niestandardową dyrektywę, aby ograniczyć pole wejściowe tylko do liczby. Dodano również atrybuty wejściowe max i min. Będzie działać również w kątowej 7.

Kątowy

    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

Nowoczesne podejście do najlepszej odpowiedzi (bez przestarzałego e.keyCode):

@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
To jest niesamowite! @Directive ({selector: "[inputNumericInput]"}) export class NumericInputDirective {@HostListener ()}
Nate,

1
Działa dobrze. Jedyny efekt uboczny obserwowany podczas kopiowania wklejania. Umożliwia kopiowanie i wklejanie zewnętrznych ciągów nienumerycznych. Przeszukałem go i znalazłem lepsze rozwiązanie, które dotyczy tego @ stackblitz.com/edit/ ...
vinsinraw

4

Arbitralna dyrektywa RegExp

Oto mała dyrektywa, która używa dowolnego wyrażenia regularnego i blokuje użytkownikowi wpisanie nieprawidłowej wartości

Aby zamaskować tylko liczby, użyj

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

Niestety, możesz oszukać to rozwiązanie, spamując daszkiem + cokolwiek chcesz napisać.
ProgFroz

3

Po prostu utwórz dyrektywę i dodaj poniżej hostlistener:

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

Zastąp nieprawidłowy tekst pustym tekstem. Wszystkie klucze i kombinacje klawiszy będą teraz działać we wszystkich przeglądarkach aż do IE9.


Jeśli zaczniesz pisać od znaku, znak nie zostanie dołączony, ale długość modelu zajmie 1. Jak to rozwiązać ?. Również jeśli element ma maksymalną długość, a następnie skopiuj i wklej zawartość mieszaną, liczba modeli będzie miała maksymalną długość. Na przykład maksymalna długość wynosi 10, a następnie skopiuj i wklej 1238261jhgjh12987 do wejścia, doda tylko 123816, ale długość modelu zajmie 10. Jakieś rozwiązania?
Satheesh Natarajan

3

Wzorzec prawidłowego numeru telefonu komórkowego ('^ ((\ + 91 -?) | 0)? [0-9] {10} $')

Wzorzec akceptacji tylko numeru z wzorca pola tekstowego („[0-9] *”)

wzór akceptacji tylko numeru z określonym numerem, np .: Kod PIN. wzorzec ('^ [0-9] {5} $')


2

Dokonałem pewnych modyfikacji w powyższej dyrektywie i zaimplementowałem 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();
    }

  }
}

jak podać wartość maksymalnej długości z pola wejściowego
Jason Brody

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

1

z odpowiedzi @omeralper. Zmienię trochę, żeby nie akceptować okresowego ascii (kod klucza 110,190). i użyj let ch = (e.key); w porównaniu z wyrażeniami regularnymi, gdy zmieniasz język (np. język tajski lub japoński), nie będzie akceptować znaków tego języka

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

Mam nadzieję, że to pomoże :)


1

Możesz utworzyć ten Walidator i zaimportować go do swojego komponentu.
Zasadniczo sprawdza poprawność ciągu wejściowego formularza:

  • sprawdź, czy nie ma kropki
  • konwertuje ciąg na liczbę
  • czek jest liczbą całkowitą
  • check jest większa od zera

Aby zaimplementować to w swoim projekcie:

  1. sugerowana ścieżka w folderze aplikacji: src / app / validators / number.validator.ts
  2. importować do swojego komponentu

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

  3. dodaj go do kontrolki formularza
    inputNumber: ['', [NumberValidator.isInteger]],
  4. jeśli nie chcesz wyświetlać nieprawidłowego znaku, powiąż znak (change)="deleteCharIfInvalid()"z wejściem, jeśli form.get('inputNumber').hasError('isInteger')tak true, usuń ostatni wstawiony znak.
// 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;
    }        
}

Nie Number.isInteger(Math.floor(control.value))zawsze będzie prawdą? Myślę, że powinno być parseFloatzamiast tego.
AndyTheEntity

1

Z obsługą dezynfekcji wklejonej zawartości:

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

Oto prosta: Dyrektywa prosta W zdarzeniu keydown sprawdza, czy długość klucza wynosi jeden, a klucz nie jest liczbą do preventDefault()i nie renderuje tego znaku.

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

Ograniczenia: Umożliwi to wklejanie za pomocą myszy w ten sposób, aby akceptować inne znaki. Aby tego uniknąć, możesz przekazać model jako dane wejściowe do dyrektywy ingOnChage zmienić wartość modelu tylko na liczby:

Jak poniżej:

EDYCJA: Dodano kod, aby wykryć zmianę w modelu i zaktualizować wartość wejścia

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

Jeśli zaczniesz pisać od znaku, znak nie zostanie dołączony, ale długość modelu wynosi 1. Jak to rozwiązać?
Satheesh Natarajan

kiedy sprawdzasz długość, pozostaje ona 0 w dyrektywie przed i po wprowadzeniu zmian. Jeśli w którymś momencie jest to jeden, powinien szybko wrócić do 0.
Lahar Shah

Nie, nie jest. Po prostu spróbuj powiązać numModel.length w szablonie i sprawdź liczbę długości
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();
          }
        };

      }
    }

Dyrektywa to także najlepszy sposób na zrobienie tego


1

Przesyłanie, ponieważ działa również z początkowymi 0, takimi jak 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, ''));
  }
}

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

lub: 2. w pliku HTML:

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

w pliku ts:

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

To 2 rozwiązanie działa


Użyj bloków kodu, aby sformatować fragmenty kodu.
YuS

0

fromCharCode zwraca „a” po naciśnięciu klawisza numerycznego „1”, więc należy unikać tej metody

(administrator: nie mógł jak zwykle komentować)


0

Widziałem wiele komentarzy dotyczących obsługi kopiowania / wklejania.

Aby wycofać się z odpowiedzi @omeralper, możesz dodać procedurę obsługi zdarzenia wklejania do dyrektywy onlyNumber, aby obsługiwać kopiowanie / wklejanie:

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

Umożliwi to kopiowanie i wklejanie treści do pola tekstowego TYLKO wtedy, gdy jest to liczba. To najprostsze rozwiązanie. Zmiana zawartości schowka w celu usunięcia wartości nienumerycznych jest znacznie bardziej skomplikowana i może nie być tego warta.

Aby wkleić tekst z IE, możesz skorzystać z:

window.clipboardData.getData('Text');


0

Samo napisanie nie byłoby łatwe

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

}


0

Możesz również utworzyć dyrektywę, która implementuje interfejs ControlValueAccessor ( https://angular.io/api/forms/ControlValueAccessor ).

Zobacz przykład roboczy tutaj: https://stackblitz.com/edit/angular-input-field-to-accept-only-numbers

Możesz słuchać zdarzenia „input” i nie ma potrzeby sprawdzania kodów dostępu. Obsługuje kopiowanie i wklejanie oraz ładnie integruje się z API Angular Forms dzięki interfejsowi ControlValueAccessor.

Dyrektywa:

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

Stosowanie:

<input name="number" type="text" onlyNumber [(ngModel)]="someNumber">
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.