如何只允许在输入中输入数字(数字和小数点)?

时间:2013-09-26 18:58:44

标签: angularjs angularjs-directive

我是angularjs的新手。我想知道只允许在文本框中输入有效数字的方法是什么。 例如,用户可以输入“1.25”,但不能输入“1.a”或“1 ..”。当用户尝试输入将使其成为无效数字的下一个字符时,他无法输入。

提前致谢。

18 个答案:

答案 0 :(得分:104)

我写了一个working CodePen example来演示一种过滤数字用户输入的好方法。该指令目前只允许正整数,但可以很容易地更新正则表达式以支持任何所需的数字格式。

我的指令很容易使用:

<input type="text" ng-model="employee.age" valid-number />

该指令很容易理解:

var app = angular.module('myApp', []);

app.controller('MainCtrl', function($scope) {
});

app.directive('validNumber', function() {
  return {
    require: '?ngModel',
    link: function(scope, element, attrs, ngModelCtrl) {
      if(!ngModelCtrl) {
        return; 
      }

      ngModelCtrl.$parsers.push(function(val) {
        if (angular.isUndefined(val)) {
            var val = '';
        }
        var clean = val.replace( /[^0-9]+/g, '');
        if (val !== clean) {
          ngModelCtrl.$setViewValue(clean);
          ngModelCtrl.$render();
        }
        return clean;
      });

      element.bind('keypress', function(event) {
        if(event.keyCode === 32) {
          event.preventDefault();
        }
      });
    }
  };
});

我想强调,保持模型参考不在指令中很重要。

我希望你觉得这很有用。

非常感谢Sean Christe和Chris Grimes向我介绍ngModelController

答案 1 :(得分:52)

您可以尝试使用此指令来阻止任何无效字符输入到输入字段中。 (更新:这依赖于明确了解模型的指令,这对于可重用性并不理想,请参阅下面的可重用示例)

app.directive('isNumber', function () {
    return {
        require: 'ngModel',
        link: function (scope) {    
            scope.$watch('wks.number', function(newValue,oldValue) {
                var arr = String(newValue).split("");
                if (arr.length === 0) return;
                if (arr.length === 1 && (arr[0] == '-' || arr[0] === '.' )) return;
                if (arr.length === 2 && newValue === '-.') return;
                if (isNaN(newValue)) {
                    scope.wks.number = oldValue;
                }
            });
        }
    };
});

它也考虑了这些情况:

  1. 从非空有效字符串转到空字符串
  2. 负值
  3. 负十进制值
  4. 我在这里创建了一个jsFiddle,以便您了解它的工作原理。

    <强>更新

    关于Adam Thomas关于不直接在指令中包含模型引用的反馈(我也认为这是最好的方法),我更新了我的jsFiddle以提供一种不依赖于此的方法。

    该指令将本地范围的双向绑定用于父范围。对指令内变量的更改将反映在父作用域中,反之亦然。

    HTML:

    <form ng-app="myapp" name="myform" novalidate> 
        <div ng-controller="Ctrl">
            <number-only-input input-value="wks.number" input-name="wks.name"/>
        </div>
    </form>
    

    角度代码:

    var app = angular.module('myapp', []);
    
    app.controller('Ctrl', function($scope) {
        $scope.wks =  {number: 1, name: 'testing'};
    });
    app.directive('numberOnlyInput', function () {
        return {
            restrict: 'EA',
            template: '<input name="{{inputName}}" ng-model="inputValue" />',
            scope: {
                inputValue: '=',
                inputName: '='
            },
            link: function (scope) {
                scope.$watch('inputValue', function(newValue,oldValue) {
                    var arr = String(newValue).split("");
                    if (arr.length === 0) return;
                    if (arr.length === 1 && (arr[0] == '-' || arr[0] === '.' )) return;
                    if (arr.length === 2 && newValue === '-.') return;
                    if (isNaN(newValue)) {
                        scope.inputValue = oldValue;
                    }
                });
            }
        };
    });
    

答案 2 :(得分:44)

首先非常感谢Adam thomas 我使用了相同的Adam的逻辑,只需要一个小的修改就可以接受十进制值。

注意:这将允许只有2个十进制值的数字

这是我的Working Example

<强> HTML

<input type="text" ng-model="salary" valid-number />

<强>的Javascript

    var app = angular.module('myApp', []);

    app.controller('MainCtrl', function($scope) {
    });

    app.directive('validNumber', function() {
      return {
        require: '?ngModel',
        link: function(scope, element, attrs, ngModelCtrl) {
          if(!ngModelCtrl) {
            return; 
          }

          ngModelCtrl.$parsers.push(function(val) {
            if (angular.isUndefined(val)) {
                var val = '';
            }
            var clean = val.replace(/[^0-9\.]/g, '');
            var decimalCheck = clean.split('.');

            if(!angular.isUndefined(decimalCheck[1])) {
                decimalCheck[1] = decimalCheck[1].slice(0,2);
                clean =decimalCheck[0] + '.' + decimalCheck[1];
            }

            if (val !== clean) {
              ngModelCtrl.$setViewValue(clean);
              ngModelCtrl.$render();
            }
            return clean;
          });

          element.bind('keypress', function(event) {
            if(event.keyCode === 32) {
              event.preventDefault();
            }
          });
        }
      };
    });

答案 3 :(得分:10)

使用step标记将最小可变值设置为某个十进制数:

e.g。 步骤= “0.01”

<input type="number" step="0.01" min="0" class="form-control" 
name="form_name" id="your_id" placeholder="Please Input a decimal number" required>

这里有一些文档:

http://blog.isotoma.com/2012/03/html5-input-typenumber-and-decimalsfloats-in-chrome/

答案 4 :(得分:7)

DEMO - - jsFiddle

指令

   .directive('onlyNum', function() {
      return function(scope, element, attrs) {

         var keyCode = [8,9,37,39,48,49,50,51,52,53,54,55,56,57,96,97,98,99,100,101,102,103,104,105,110];
          element.bind("keydown", function(event) {
            console.log($.inArray(event.which,keyCode));
            if($.inArray(event.which,keyCode) == -1) {
                scope.$apply(function(){
                    scope.$eval(attrs.onlyNum);
                    event.preventDefault();
                });
                event.preventDefault();
            }

        });
     };
  });

<强> HTML

 <input type="number" only-num>

注意:不要忘记包含角度js的jQuery

答案 5 :(得分:6)

您可以轻松使用ng-pattern。

ng-pattern="/^[1-9][0-9]{0,2}(?:,?[0-9]{3}){0,3}(?:\.[0-9]{1,2})?$/"

答案 6 :(得分:5)

有一个输入数字指令,我相信它可以做你想要的。

<input type="number"
   ng-model="{string}"
   [name="{string}"]
   [min="{string}"]
   [max="{string}"]
   [required]
   [ng-required="{string}"]
   [ng-minlength="{number}"]
   [ng-maxlength="{number}"]
   [ng-pattern="{string}"]
   [ng-change="{string}"]>

官方文档在这里:http://docs.angularjs.org/api/ng.directive:input.number

答案 7 :(得分:4)

HTML

 <input type="text" name="number" only-digits>

//输入123

  .directive('onlyDigits', function () {
    return {
      require: 'ngModel',
      restrict: 'A',
      link: function (scope, element, attr, ctrl) {
        function inputValue(val) {
          if (val) {
            var digits = val.replace(/[^0-9]/g, '');

            if (digits !== val) {
              ctrl.$setViewValue(digits);
              ctrl.$render();
            }
            return parseInt(digits,10);
          }
          return undefined;
        }            
        ctrl.$parsers.push(inputValue);
      }
    };

//键入:123或123.45

 .directive('onlyDigits', function () {
    return {
      require: 'ngModel',
      restrict: 'A',
      link: function (scope, element, attr, ctrl) {
        function inputValue(val) {
          if (val) {
            var digits = val.replace(/[^0-9.]/g, '');

            if (digits !== val) {
              ctrl.$setViewValue(digits);
              ctrl.$render();
            }
            return parseFloat(digits);
          }
          return undefined;
        }            
        ctrl.$parsers.push(inputValue);
      }
    };

答案 8 :(得分:2)

我想要一个可能受minmax属性限制的指令,如下所示:

<input type="text" integer min="1" max="10" />

所以我写了以下内容:

.directive('integer', function() {
    return {
        restrict: 'A',
        require: '?ngModel',
        link: function(scope, elem, attr, ngModel) {
            if (!ngModel)
                return;

            function isValid(val) {
                if (val === "")
                    return true;

                var asInt = parseInt(val, 10);
                if (asInt === NaN || asInt.toString() !== val) {
                    return false;
                }

                var min = parseInt(attr.min);
                if (min !== NaN && asInt < min) {
                    return false;
                }

                var max = parseInt(attr.max);
                if (max !== NaN && max < asInt) {
                    return false;
                }

                return true;
            }

            var prev = scope.$eval(attr.ngModel);
            ngModel.$parsers.push(function (val) {
                // short-circuit infinite loop
                if (val === prev)
                    return val;

                if (!isValid(val)) {
                    ngModel.$setViewValue(prev);
                    ngModel.$render();
                    return prev;
                }

                prev = val;
                return val;
            });
        }
    };
});

答案 9 :(得分:1)

这对我来说似乎最容易: http://jsfiddle.net/thomporter/DwKZh/

(代码不是我的,我不小心偶然发现了它)

    angular.module('myApp', []).directive('numbersOnly', function(){
       return {
         require: 'ngModel',
         link: function(scope, element, attrs, modelCtrl) {
           modelCtrl.$parsers.push(function (inputValue) {
               // this next if is necessary for when using ng-required on your input. 
               // In such cases, when a letter is typed first, this parser will be called
               // again, and the 2nd time, the value will be undefined
               if (inputValue == undefined) return '' 
               var transformedInput = inputValue.replace(/[^0-9]/g, ''); 
               if (transformedInput!=inputValue) {
                  modelCtrl.$setViewValue(transformedInput);
                  modelCtrl.$render();
               }         

               return transformedInput;         
           });
         }
       };
    });

答案 10 :(得分:1)

我修改了Alan的上述答案,将数量限制在指定的最小值/最大值。如果输入范围之外的数字,它将在1500ms后设置最小值或最大值。如果您完全清除该字段,则不会设置任何内容。

HTML:

<input type="text" ng-model="employee.age" min="18" max="99" valid-number />

使用Javascript:

var app = angular.module('myApp', []);

app.controller('MainCtrl', function($scope) {});

app.directive('validNumber', function($timeout) {
    return {
        require: '?ngModel',
        link: function(scope, element, attrs, ngModelCtrl) {
            if (!ngModelCtrl) {
                return;
            }

            var min = +attrs.min;
            var max = +attrs.max;
            var lastValue = null;
            var lastTimeout = null;
            var delay = 1500;

            ngModelCtrl.$parsers.push(function(val) {
                if (angular.isUndefined(val)) {
                    val = '';
                }            

                if (lastTimeout) {
                    $timeout.cancel(lastTimeout);
                }

                if (!lastValue) {
                    lastValue = ngModelCtrl.$modelValue;
                }

                if (val.length) {
                    var value = +val;
                    var cleaned = val.replace( /[^0-9]+/g, ''); 

                    // This has no non-numeric characters
                    if (val.length === cleaned.length) {
                        var clean = +cleaned;

                        if (clean < min) {
                            clean = min;
                        } else if (clean > max) {
                            clean = max;
                        }

                        if (value !== clean || value !== lastValue) {
                            lastTimeout = $timeout(function () {
                                lastValue = clean;
                                ngModelCtrl.$setViewValue(clean);
                                ngModelCtrl.$render();
                            }, delay);
                        }
                    // This has non-numeric characters, filter them out
                    } else {
                        ngModelCtrl.$setViewValue(lastValue);
                        ngModelCtrl.$render();
                    }
                }

                return lastValue;
            });

            element.bind('keypress', function(event) {
                if (event.keyCode === 32) {
                    event.preventDefault();
                }
            });

            element.on('$destroy', function () {
                element.unbind('keypress');
            });
        }
    };
});

答案 11 :(得分:1)

这是我真正快速肮脏的一个:

<!-- HTML file -->
<html ng-app="num">
  <head></head>
  <body ng-controller="numCtrl">
    <form class="digits" name="digits" ng-submit="getGrades()" novalidate >
      <input type="text" placeholder="digits here plz" name="nums" ng-model="nums" required ng-pattern="/^(\d)+$/" />
      <p class="alert" ng-show="digits.nums.$error.pattern">Numbers only, please.</p> 
      <br>
      <input type="text" placeholder="txt here plz" name="alpha" ng-model="alpha" required ng-pattern="/^(\D)+$/" />
      <p class="alert" ng-show="digits.alpha.$error.pattern">Text only, please.</p>
      <br>
      <input class="btn" type="submit" value="Do it!" ng-disabled="!digits.$valid" />
    </form>
  </body>
</html>

// Javascript file
var app = angular.module('num', ['ngResource']);
app.controller('numCtrl', function($scope, $http){
  $scope.digits = {};
});

这要求您包含角度资源库,以便对字段进行持久绑定以进行验证。

Working example here

在1.2.0-rc.3 +中扮演冠军。修改正则表达式,你应该全部设置。也许像/^(\d|\.)+$/这样的东西?与往常一样,在完成后验证服务器端。

答案 12 :(得分:1)

我遇到了类似的问题并更新了角度文档的input[type="number"]示例,以及使用此方法解决它的问题。

PS:快速提醒一下,浏览器支持字符&#39; e&#39;和&#39; E&#39;在输入[type =&#34; number&#34;]中,因为keypress事件是必需的。

angular.module('numfmt-error-module', [])
.directive('numbersOnly', function() {
  return {
    require: 'ngModel',
    scope: {
      precision: '@'
    },
    link: function(scope, element, attrs, modelCtrl) {
      var currencyDigitPrecision = scope.precision;

      var currencyDigitLengthIsInvalid = function(inputValue) {
        return countDecimalLength(inputValue) > currencyDigitPrecision;
      };

      var parseNumber = function(inputValue) {
        if (!inputValue) return null;
        inputValue.toString().match(/-?(\d+|\d+.\d+|.\d+)([eE][-+]?\d+)?/g).join('');
        var precisionNumber = Math.round(inputValue.toString() * 100) % 100;

        if (!!currencyDigitPrecision && currencyDigitLengthIsInvalid(inputValue)) {
          inputValue = inputValue.toFixed(currencyDigitPrecision);
          modelCtrl.$viewValue = inputValue;
        }
        return inputValue;
      };

      var countDecimalLength = function (number) { 
         var str = '' + number;
         var index = str.indexOf('.');
         if (index >= 0) {
           return str.length - index - 1;
         } else {
           return 0;
         }
      };

      element.on('keypress', function(evt) {
        var charCode, isACommaEventKeycode, isADotEventKeycode, isANumberEventKeycode;
        charCode = String.fromCharCode(evt.which || event.keyCode);
        isANumberEventKeycode = '0123456789'.indexOf(charCode) !== -1;
        isACommaEventKeycode = charCode === ',';
        isADotEventKeycode = charCode === '.';

        var forceRenderComponent = false;

        if (modelCtrl.$viewValue != null && !!currencyDigitPrecision) {
          forceRenderComponent = currencyDigitLengthIsInvalid(modelCtrl.$viewValue);
        }

        var isAnAcceptedCase = isANumberEventKeycode || isACommaEventKeycode || isADotEventKeycode;

        if (!isAnAcceptedCase) {
          evt.preventDefault();
        }

        if (forceRenderComponent) {
          modelCtrl.$render(modelCtrl.$viewValue);
        }

        return isAnAcceptedCase;
      });

      modelCtrl.$render = function(inputValue) {
        return element.val(parseNumber(inputValue));
      };

      modelCtrl.$parsers.push(function(inputValue) {

        if (!inputValue) {
          return inputValue;
        }

        var transformedInput;
        modelCtrl.$setValidity('number', true);
        transformedInput = parseNumber(inputValue);

        if (transformedInput !== inputValue) {

          modelCtrl.$viewValue = transformedInput;
          modelCtrl.$commitViewValue();
          modelCtrl.$render(transformedInput);
        }
        return transformedInput;
      });
    }
  };
});

在您的HTML中,您可以使用此方法

<input 
  type="number" 
  numbers-only 
  precision="2" 
  ng-model="model.value" 
  step="0.10" />

Here is the plunker with this snippet

答案 13 :(得分:0)

从gordy的回答中扩展:

干得好btw。但它也允许+在前面。这将删除它。

    scope.$watch('inputValue', function (newValue, oldValue) {
        var arr = String(newValue).split("");
        if (arr.length === 0) return;
        if (arr.length === 1 && (arr[0] == '-' || arr[0] === '.')) return;
        if (arr.length === 2 && newValue === '-.') return;
        if (isNaN(newValue)) {
            scope.inputValue = oldValue;
        }
        if (arr.length > 0) {
            if (arr[0] === "+") {
                scope.inputValue = oldValue;
            }
        }
    });

答案 14 :(得分:0)

这是一个衍生物,它也会阻止两次输入的小数点

HTML

    <input tabindex="1" type="text" placeholder="" name="salary" id="salary" data-ng-model="salary" numbers-only="numbers-only" required="required">

    var app = angular.module("myApp", []);

    app.directive('numbersOnly', function() {
    return {
            require : 'ngModel', link : function(scope, element, attrs,  modelCtrl) {
        modelCtrl.$parsers.push(function(inputValue) {

            if (inputValue == undefined) {
                return ''; //If value is required
            }

            // Regular expression for everything but [.] and [1 - 10] (Replace all)
            var transformedInput = inputValue.replace(/[a-z!@#$%^&*()_+\-=\[\]{};':"\\|,<>\/?]/g, '');

            // Now to prevent duplicates of decimal point
            var arr = transformedInput.split('');

            count = 0; //decimal counter
            for ( var i = 0; i < arr.length; i++) {
                if (arr[i] == '.') {
                    count++; //  how many do we have? increment
                }
            }

            // if we have more than 1 decimal point, delete and leave only one at the end
            while (count > 1) {
                for ( var i = 0; i < arr.length; i++) {
                    if (arr[i] == '.') {
                        arr[i] = '';
                        count = 0;
                        break;
                    }
                }
            }

            // convert the array back to string by relacing the commas
            transformedInput = arr.toString().replace(/,/g, '');

            if (transformedInput != inputValue) {
                modelCtrl.$setViewValue(transformedInput);
                modelCtrl.$render();
            }

            return transformedInput;
        });
    }
};
});

答案 15 :(得分:0)

扩展Adam Thomas的回答你可以通过使用自定义regexp添加输入参数来轻松地使这个指令更通用:

var app = angular.module('myApp', []);

app.controller('MainCtrl', function($scope) {
});

app.directive('validInput', function() {
  return {
    require: '?ngModel',
    scope: {
      "inputPattern": '@'
    },
    link: function(scope, element, attrs, ngModelCtrl) {

      var regexp = null;

      if (scope.inputPattern !== undefined) {
        regexp = new RegExp(scope.inputPattern, "g");
      }

      if(!ngModelCtrl) {
        return;
      }

      ngModelCtrl.$parsers.push(function(val) {
        if (regexp) {
          var clean = val.replace(regexp, '');
          if (val !== clean) {
            ngModelCtrl.$setViewValue(clean);
            ngModelCtrl.$render();
          }
          return clean;
        }
        else {
          return val;
        }

      });

      element.bind('keypress', function(event) {
        if(event.keyCode === 32) {
          event.preventDefault();
        }
      });
    }
}});

HTML

<input type="text" ng-model="employee.age" valid-input  
       input-pattern="[^0-9]+" placeholder="Enter an age" />
</label>

住在CodePen

答案 16 :(得分:0)

请查看我的组件,它将帮助您仅允许特定的数据类型。目前支持整数,小数,字符串和时间(HH:MM)。

  • UPDATE - 允许字符串使用可选的最大长度
  • string - 仅允许整数使用可选的最大值
  • integer - 仅允许带有可选小数点和最大值的小数(默认为2位小数)
  • decimal - 24小时仅允许时间格式(HH:MM)

https://github.com/ksnimmy/txDataType

希望有所帮助。

答案 17 :(得分:0)

DECIMAL

directive('decimal', function() {
                return {
                    require: 'ngModel',
                    restrict: 'A',
                    link: function(scope, element, attr, ctrl) {
                        function inputValue(val) {
                            if (val) {
                                var digits = val.replace(/[^0-9.]/g, '');

                                if (digits.split('.').length > 2) {
                                    digits = digits.substring(0, digits.length - 1);
                                }

                                if (digits !== val) {
                                    ctrl.$setViewValue(digits);
                                    ctrl.$render();
                                }
                                return parseFloat(digits);
                            }
                            return "";
                        }
                        ctrl.$parsers.push(inputValue);
                    }
                };
            });

DIGITS

directive('entero', function() {
            return {
                require: 'ngModel',
                restrict: 'A',
                link: function(scope, element, attr, ctrl) {
                    function inputValue(val) {
                        if (val) {
                            var value = val + ''; //convert to string
                            var digits = value.replace(/[^0-9]/g, '');

                            if (digits !== value) {
                                ctrl.$setViewValue(digits);
                                ctrl.$render();
                            }
                            return parseInt(digits);
                        }
                        return "";
                    }
                    ctrl.$parsers.push(inputValue);
                }
            };
        });

angular directives for validate numbers