Angular2 - 输入字段仅接受数字

Ani*_*dhe 62 html input angularjs-directive angular

在Angular2中,如何屏蔽输入字段(文本框),使其仅接受数字而不接受字母?

我有以下HTML输入:

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

上面的输入是通用文本输入,可以用作简单文本字段或数字字段(例如显示年份).

使用angular2,我如何使用相同的输入控件并在此字段上应用某种过滤器/蒙版,这样它只接受数字?我可以通过哪些不同方式实现这一目标?

注意:我需要仅使用文本框而不使用输入数字类型来实现此目的.

ome*_*per 95

您可以使用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();
        }
      }
  }
}
Run Code Online (Sandbox Code Playgroud)

并且您需要在输入中将指令名称作为属性编写

<input OnlyNumber="true" />
Run Code Online (Sandbox Code Playgroud)

不要忘记在模块的声明数组中编写指令.

通过使用正则表达式,您仍然需要功能键

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();
      }
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 它不允许复制粘贴. (3认同)
  • keyCode 已弃用 https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode (3认同)

ras*_*dnk 50

如果你不想要一个指令

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

在component.html中

<input (keypress)="numberOnly($event)" type="text">
Run Code Online (Sandbox Code Playgroud)

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

  }
}
Run Code Online (Sandbox Code Playgroud)

  • 这种方法的问题在于关键事件不会捕获用户粘贴或浏览器自动填充输入字段.所以这是一个糟糕的解决方案. (14认同)

Jea*_* A. 28

我想建立@omeralper给出的答案,我认为这为一个可靠的解决方案提供了良好的基础.

我建议的是一个简化的最新版本,其中包含最新的Web标准.请务必注意,event.keycode已从Web标准中删除,未来的浏览器更新可能不再支持它.请参阅https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode

此外,该方法

String.fromCharCode(e.keyCode);
Run Code Online (Sandbox Code Playgroud)

不保证与用户按下的键有关的keyCode映射到用户键盘上标识的预期字母,因为不同的键盘配置将导致特定的键码不同的字符.使用它会引入难以识别的错误,并且很容易破坏某些用户的功能.相反,我建议使用event.key,请参阅此处的文档https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key

此外,我们只希望结果输出是有效的小数.这意味着应接受数字1,11.2,5000.2341234,但不应接受值1.1.2.

请注意,在我的解决方案中,我不包括剪切,复制和粘贴功能,因为它打开了窗口中的错误,尤其是当人们在相关字段中粘贴不需要的文本时.这需要在keyup处理程序上进行清理过程; 这不是这个帖子的范围.

这是我提出的解决方案.

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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Elv*_*dez 21

我知道这是一个老问题,但由于这是一个常见的功能,我想分享我所做的修改:

  • 自定义小数点分隔符(点或逗号)
  • 仅支持整数或整数和小数
  • 仅支持正数或正数和负数
  • 验证减号( - )在开头
  • 支持鼠标粘贴(虽然有一些限制,但https://caniuse.com/#feat=clipboard)
  • 支持Mac命令键
  • 替换".33"和"33"之类的字符串.对于正确的版本:0.33和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);
                }
            }
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

用法:

 <input NumbersOnly
        [allowDecimals]="true"
        [allowSign]="true"
        type="text">
Run Code Online (Sandbox Code Playgroud)


ket*_*han 13

<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();
    }
  }
Run Code Online (Sandbox Code Playgroud)


Ben*_*apa 13

更简洁的解决方案.试试这个指令.

如果您正在使用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, ''));
  }
}
Run Code Online (Sandbox Code Playgroud)

在输入上使用它如下:

<input matInput formControlName="aNumberField" numberOnly>
Run Code Online (Sandbox Code Playgroud)

  • 虽然此解决方案有效,但它会触发模型更改事件两次,也就是说使用正则表达式的方法是正确的,这是一个不会触发模型更改事件两次的版本:https://stackblitz.com/edit/angular-numbers-仅指令?file=app%2Fnumbers-only.directive.ts (3认同)

Jas*_*n W 11

我知道这有很多答案,但我需要处理以下问题(似乎没有一个答案完全支持):

  • 支持 textarea 并选择多行
  • 小数或负数
  • 每行最大长度
  • 跨浏览器支持(Chrome、Edge、IE 11)
  • 处理剪切/粘贴操作和事件

该解决方案允许我像​​这样定义一个 textarea:

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

或者如果我只想要正整数

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

这是我的指令:

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

}
Run Code Online (Sandbox Code Playgroud)


小智 8

您需要使用type ="number"而不是文本.您还可以指定最大和最小数字

<input type="number" name="quantity" min="1" max="5">
Run Code Online (Sandbox Code Playgroud)

  • `type ="number"的缺点是它接受字符`e`作为科学记数法的一部分 (5认同)
  • 它在Firefox中也接受字母表 (3认同)
  • 我想在不使用数字类型的情况下实现此目的. (2认同)
  • 如以下答案中所述,对数字类型的支持仍然很麻烦:http://stackoverflow.com/a/14995890/1156185 (2认同)

Par*_*ain 8

你可以像这样实现它

<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;
}
Run Code Online (Sandbox Code Playgroud)

希望这会帮助你.


Ago*_*eca 8

最佳答案的现代方法(不推荐使用 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();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 效果很好。仅在复制粘贴中观察到副作用。它允许复制粘贴外部非数字字符串。谷歌搜索并找到了一个更好的解决方案来解决这个问题@ https://stackblitz.com/edit/angular-numbers-only-directive?file=app%2Fnumbers-only.directive.ts (3认同)
  • 这太棒了!@Directive({ 选择器: "[inputNumericInput]" }) 导出类 NumericInputDirective { @HostListener() } (2认同)

Beh*_*imi 6

使用pattern属性进行输入,如下所示:

<input type="text" pattern="[0-9]+" >
Run Code Online (Sandbox Code Playgroud)


abo*_*hdi 6

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

或: 2. 在 HTML 文件中:

 <input [(ngModel)]="data" (keypress)="stripText($event)"
     class="form-control">
Run Code Online (Sandbox Code Playgroud)

在 ts 文件中:

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

这 2 个解决方案有效


Aat*_*eya 5

您可以使用正则表达式:

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

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


leo*_*eox 5

感谢 JeanPaul A. 和 rdanielmurphy。我已经制定了自己的自定义指令,用于将输入字段限制为仅数字。还添加了 max 和 min 输入属性。也将在角度 7 下工作。

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

}
Run Code Online (Sandbox Code Playgroud)

HTML

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


Kam*_*ski 5

任意 RegExp 指令

这是使用任意正则表达式并阻止用户键入无效值的小指令

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

@Directive({selector: '[allowedRegExp]'})
export class AllowedRegExpDirective {
    
  @Input() allowedRegExp: string;
  
  @HostListener('keydown', ['$event']) onKeyDown(event: any) {
    // case: selected text (by mouse) - replace it
    let s= event.target.selectionStart;
    let e= event.target.selectionEnd;
    let k= event.target.value + event.key;
        
    if(s!=e) {
      k= event.target.value
      k= k.slice(0,s) + event.key + k.slice(e,k.length);
    }

    // case: special characters (ignore)
    if(['ArrowLeft','ArrowRight','ArrowUp','ArroDown','Backspace','Tab','Alt'
       'Shift','Control','Enter','Delete','Meta'].includes(event.key)) return;

    // case: normal situation - chceck regexp
    let re = new RegExp(this.allowedRegExp);
        
    if(!re.test(k)) event.preventDefault();
  }
}
Run Code Online (Sandbox Code Playgroud)

只屏蔽数字使用

<input [allowedRegExp]="'^[0-9]*$'" type="text" ... >
Run Code Online (Sandbox Code Playgroud)