【问题标题】:Angular2 - Input Field To Accept Only NumbersAngular2 - 仅接受数字的输入字段
【发布时间】:2017-05-18 20:17:52
【问题描述】:

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

我有以下 HTML 输入:

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

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

使用 Angular 2,我如何使用相同的输入控件并在此字段上应用某种过滤器/掩码,使其仅接受数字?

我可以通过哪些不同的方式来实现这一目标?

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

【问题讨论】:

标签: html angular input angularjs-directive


【解决方案1】:

您可以使用 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();
        }
      }
  }
}

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

<input OnlyNumber="true" />

不要忘记将指令写入模块的声明数组中。

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

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

【讨论】:

  • 太好了。有什么方法可以使用 RegEx 模式实现相同的目标?
  • @Shardul 只需将(e.keyCode == 86 &amp;&amp; e.ctrlKey === true) 添加到条件中,复制有效但粘贴无效
  • 我为.- 添加了e.keyCode == 109 || e.keyCode ==190,但- 不被接受?
  • 它工作得很好..但是当你使用鼠标粘贴时它允许非数字..所以鼠标事件可能可以单独处理。谢谢奥马尔珀
【解决方案2】:

如果你不想要指令

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

在component.html中

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

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

  }
}

【讨论】:

  • 这种方法的问题是关键事件不会捕获用户粘贴或浏览器自动填充输入字段。所以这是一个糟糕的解决方案。
【解决方案3】:

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

  • 自定义小数分隔符(点或逗号)
  • 仅支持整数或整数和小数
  • 仅支持正数或正数和负数
  • 验证减号 (-) 在开头
  • 支持鼠标粘贴(虽然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);
                }
            }
        }
    }
    

用法:

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

【讨论】:

  • 我更改了 validatevalue 方法的最后一行,以防止为无效粘贴添加零。如果(有效){ this.hostElement.nativeElement['value'] = value;}
  • 能否也添加拖放验证?另外,我注意到输入字段值确实更改为前导和尾随小数点分隔符的 0 填充值,但该值不会在双向绑定变量中更新。例如: [(NgModel)]="myVariable" ,在这里,如果我们在输入字段中输入 .3 ,文本输入中的值会在模糊时变为 0.3,但 myVariable 中的值仍保持为 '.3' 。
  • Delete and Enter 输入丢失,但无论如何解决方法都很好
  • @SushmitSagar 为了确保 2 路数据绑定工作添加:@Output() ngModelChange:EventEmitter = new EventEmitter();声明,然后添加: this.ngModelChange.emit(valid ? value : 0);作为 validateValue 方法的最后一行
  • 还将这些行添加到 validateValue 方法中: //删除不必要的前导零 let secondChar=value.charAt(1); if (firstCharacter=='0'&&(secondChar!=''||secondChar!=this.decimalSeparator)) { value=value.substring(1); }
【解决方案4】:

我想以@omeralper 给出的答案为基础,我认为这为可靠的解决方案提供了良好的基础。

我提议的是一个简化的和最新的版本,具有最新的网络标准。重要的是要注意 event.keycode 已从 Web 标准中删除,未来的浏览器更新可能不再支持它。见https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode

还有方法

String.fromCharCode(e.keyCode);

不保证与用户按下的键相关的 keyCode 映射到用户键盘上标识的预期字母,因为不同的键盘配置会导致特定的 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();
        }
    }
}

【讨论】:

  • 这是一个非常有趣的方法。您对如何在不使用旧方法(例如 (e.keyCode == 67 && e.ctrlKey === true) 的情况下实现复制/粘贴功能)有什么建议??
  • 我没有亲自尝试过,但是您可以类似地收听触发的复制/粘贴事件。它们生成一个 ClipboardEvent (developer.mozilla.org/en-US/docs/Web/API/ClipboardEvent),其中包含正在复制/粘贴的数据。唯一的缺点是这仍然是实验性的,仅受最新浏览器支持 - caniuse.com/#search=paste
  • 我尝试了类似的方法,但不幸的是,这并不适用于所有情况。您的“下一个”变量假定按下的字符位于当前键入值的末尾。情况并非总是如此。例如,如果有人输入 100,然后决定通过在前面附加 1 将其设为 1100。您的“下一个”变量将不正确 (1001)。
  • 由于“下一个”值仅用于检查输入金额是否为有效小数(而不是设置值),因此将其附加到末尾不会更改正则表达式验证。跨度>
  • 只有我想添加此行以应用于输入控件。
【解决方案5】:

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

如果您使用 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, ''));
  }
}

像这样在你的输入上使用它:

<input matInput formControlName="aNumberField" numberOnly>

【讨论】:

  • 虽然此解决方案有效,但它会触发两次模型更改事件,这表明使用正则表达式的方法是正确的,这是一个不会触发两次模型更改事件的版本:stackblitz.com/edit/…
  • 对 ntziolis 的评论:到目前为止,Ben Gulapa 的解决方案对我有用。但是ntziolis引用的解决方案不是。如果我错了,请原谅我,但似乎至少对我而言,上述stackblitz链接中的代码问题是我输入的最后一个不需要的字符,虽然它没有在用户界面中显示,但不知何故得到了放入我的组件的绑定变量中。只是最后一个不需要的字符。
  • 继续我的评论:使用 Angular 7 和限制为两个字符的 HMTL 输入文本。
【解决方案6】:

您需要使用 type="number" 代替文本。您还可以指定最大和最小数字

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

【讨论】:

  • 我想在不使用数字类型的情况下实现这一点。
  • 对数字类型的支持仍然有很多错误,如本答案所述:stackoverflow.com/a/14995890/1156185
  • type="number" 的缺点是它接受字符 e 作为科学记数法的一部分
  • type="number" 的缺点是它会破坏键盘并让用户猜测键盘停止工作的原因。过滤是非常反用户友好的,更好的方法是显示清晰的错误消息并保持输入完整。当用户输入 a1b2c3 时,给他结果“123”绝对没有意义
【解决方案7】:
<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();
    }
  }

【讨论】:

    【解决方案8】:

    你可以使用正则表达式:

    <input type="text" (keypress)="numericOnly($event)">
    
    numericOnly(event): boolean {    
        let patt = /^([0-9])$/;
        let result = patt.test(event.key);
        return result;
    }
    

    【讨论】:

    • 是的,这很有帮助,但我的输入字段中也需要 (.) 小数
    【解决方案9】:

    你可以这样实现它

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

    希望这会对你有所帮助。

    【讨论】:

    • 您能详细说明一下吗? event.charCode==8 在做什么?
    【解决方案10】:

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

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

    解决方案允许我像​​这样定义文本区域:

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

    或者如果我只想要正整数

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

    这是我的指令:

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

    【讨论】:

    • 很好,谢谢队友^_^
    • 我试图根据我的需要修改这个答案(不需要多行),只是意识到它对input type="number" 失败,因为 selectionStart/End 不可用。事实上,似乎没有办法在数字输入中获取插入符号的位置:(
    【解决方案11】:
    1. &lt;input oninput="this.value=this.value.replace(/[^0-9]/g,'')"

    或: 2. 在 HTML 文件中:

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

    在 ts 文件中:

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

    这 2 个解决方案有效

    【讨论】:

    • 请使用代码块格式化您的代码sn-ps。
    【解决方案12】:

    感谢 JeanPaul A. 和 rdanielmurphy。我已经制定了自己的自定义指令,将输入字段限制为仅数字。还添加了最大和最小输入属性。也可以在 Angular 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();
          }
        }
      }
    
    }
    

    HTML

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

    【讨论】:

    • 这假定光标/插入符号始终位于键入的数字的末尾。如果用户想在两者之间输入,它会失败。
    【解决方案13】:

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

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

    【讨论】:

    • 它不工作。当你开始输入时,你输入的字符是错误的
    【解决方案14】:

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

    【讨论】:

    • 这太棒了! @Directive({ selector: "[inputNumericInput]" }) 导出类 NumericInputDirective { @HostListener() }
    • 效果很好。在复制粘贴中仅观察到副作用。它允许复制粘贴外部非数字字符串。谷歌搜索并找到了一个更好的解决方案来解决这个问题@stackblitz.com/edit/…
    【解决方案15】:

    任意 RegExp 指令

    这里是小directive,它使用任意正则表达式并阻止用户输入无效值

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

    要仅屏蔽数字,请使用

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

    【讨论】:

    • 不幸的是,您可以通过向抑扬符+任何您想写的内容发送垃圾邮件来欺骗此解决方案。
    【解决方案16】:

    只需创建一个指令并在下面添加主机侦听器:

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

    用空替换无效文本。所有键和组合键现在都可以在 IE9 之前的所有浏览器中使用。

    【讨论】:

    • 如果以char开头类型,char不会追加,但模型长度的计数为1。如何解决?此外,如果元素具有最大长度,则复制并粘贴混合内容,模型计数将是最大长度。例如,最大长度为 10,然后将 1238261jhgjh12987 复制并粘贴到输入将仅附加 123816,但模型的长度将采用 10。任何解决方案?
    【解决方案17】:

    为了实现这一点,我将一个函数绑定到 onInput 方法,如下所示:

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

    这是我表单中的示例:

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

    然后我将以下函数添加到我的组件中:

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

    这个正则表达式/[^0-9]/g 搜索任何不是数字的东西,并使用.replace 我将它设置为什么都不替换。因此,当用户尝试输入一个不是数字的字符(在本例中是一个不是 0 到 9 的字符)时,文本框中似乎什么也没有发生。

    【讨论】:

      【解决方案18】:

      这里很简单:简单指令 在 keydown 事件中,它会检查键的长度是否为 1,并且键不是 preventDefault() 的数字,并且不会呈现该字符。

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

      限制:它允许使用鼠标粘贴,这样可以接受其他字符。为避免这种情况,您可以将模型作为输入传递给指令,并将 ngOnChage 传递给该模型,将值更改为仅数字:

      如下:

      编辑:添加了检测模型变化并更新输入值的代码

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

      【讨论】:

      • 如果以char开头类型,char不会追加,但模型长度的计数为1。如何解决?
      • 什么时候检查长度,修改前后指令中的长度都为0。如果在某个时候它是 1,它应该很快回到 0。
      • 不,不是。只需尝试在模板中绑定 numModel.length 并检查长度计数
      【解决方案19】:

      有效手机号码的模式 模式('^((\+91-?)|0)?[0-9]{10}$')

      仅接受文本框中数字的模式 模式('[0-9]*')

      模式只接受具有特定数字的数字,例如:Pincode。 模式('^[0-9]{5}$')

      【讨论】:

        【解决方案20】:

        您可以使用面具轻松做到这一点:

        <input type='text' mask="99" formControlName="percentage" placeholder="0">
        

        99 - 可选的 2 位数字

        别忘了在你的模块中导入 NgxMaskModule:

        imports: [
            NgxMaskModule.forRoot(),
        ]
        

        【讨论】:

          【解决方案21】:

          我对上面的指令做了一些修改,实现了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();
              }
          
            }
          }
          

          【讨论】:

          • 如何从输入字段中给出最大长度值
          • 工作
          【解决方案22】:

          铸造,因为它也有效 以 0 开头,如 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, ''));
            }
          }
          

          【讨论】:

            【解决方案23】:

            来自@omeralper 的回答。 我改变了一点,不接受句号 ascii(键码 110,190)。 并使用 let ch = (e.key);当您更改语言(例如泰语或日语)时与正则表达式进行比较,它不会接受那些语言的字符

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

            希望得到帮助:)

            【讨论】:

              【解决方案24】:

              您可以创建此验证器并将其导入您的组件中。
              基本上验证表单输入字符串:

              • 检查没有点
              • 将字符串转换为数字
              • check 是一个整数
              • 检查大于零

              在你的项目中实现它:

              1. 应用文件夹中的建议路径:src/app/validators/number.validator.ts
              2. 在你的组件中导入

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

              3. 将其添加到表单控件
                inputNumber: ['', [NumberValidator.isInteger]],
              4. 如果您不想显示无效字符,请将(change)="deleteCharIfInvalid()" 绑定到输入,如果form.get('inputNumber').hasError('isInteger')true,则删除最后插入的字符。
              // 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;
                  }        
              }
              

              【讨论】:

              • Number.isInteger(Math.floor(control.value)) 不会一直是真的吗?我认为应该改为parseFloat
              【解决方案25】:

              支持清理粘贴的内容:

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

              【讨论】:

                【解决方案26】:

                只需在文本上使用类型编号,如下所示:

                <input type="number" class="form-control" matInput name="value" placeholder="xxx" (change)="xxx()" formControlName="value">
                

                【讨论】:

                  【解决方案27】:
                   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();
                            }
                          };
                  
                        }
                      }
                  

                  指令也是最好的方法

                  【讨论】:

                    【解决方案28】:

                    您还可以创建一个实现 ControlValueAccessor 接口 (https://angular.io/api/forms/ControlValueAccessor) 的指令。

                    在此处查看工作示例:https://stackblitz.com/edit/angular-input-field-to-accept-only-numbers

                    您可以收听“输入”事件,无需检查键码。 由于 ControlValueAccessor 接口,它支持复制和粘贴,并与 Angular Forms API 很好地集成。

                    指令:

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

                    用法:

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

                    【讨论】:

                      【解决方案29】:

                      虽然有很多答案,但没有一个是简单的 npm 包

                      基于Elvis Fernandez's 的回答,在我添加了对几个边缘情况的处理之后,我创建了一个 NG 模块,您可以使用 npm 轻松安装:

                      npm i ngx-numbers-only-directive

                      如何使用:

                      在你的 appModule 中导入 NgxNumbersOnlyDirectiveModule:

                      从“ngx-numbers-only-directive”导入 { NgxNumbersOnlyDirectiveModule }

                      并将其添加到您的导入数组中:

                      导入:[NgxNumbersOnlyDirectiveModule]

                      将指令添加到输入元素。例子:

                      允许负数:

                      允许小数:

                      如果您不想添加包,指令源代码位于:

                      https://github.com/abfist/NgxNumbersOnlyDirective/tree/master/projects/ngx-numbers-only-directive/src/lib

                      【讨论】:

                        【解决方案30】:

                        当按下小键盘“1”时,fromCharCode 返回“a”,因此应避免使用此方法

                        (管理员:无法像往常一样发表评论)

                        【讨论】:

                          猜你喜欢
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 2014-06-26
                          • 2021-09-05
                          • 1970-01-01
                          • 2018-05-13
                          • 2022-01-09
                          • 2010-12-17
                          相关资源
                          最近更新 更多