Soy nuevo en angularjs. Me pregunto cuál es la forma de permitir que solo se escriba un número válido en un cuadro de texto. Por ejemplo, el usuario puede escribir “1.25”, pero no puede escribir “1.a” o “1 ..”. Cuando el usuario intente escribir el siguiente carácter que lo convertirá en un número inválido, no podrá ingresarlo.
Gracias por adelantado.
Puede probar esta directiva para evitar que se ingresen caracteres inválidos en un campo de entrada. ( Actualización : esto depende de que la directiva tenga un conocimiento explícito del modelo, que no es ideal para la reutilización, ver a continuación un ejemplo reutilizable)
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; } }); } }; });
También representa estos escenarios:
He creado un jsFiddle aquí para que pueda ver cómo funciona.
ACTUALIZAR
Siguiendo los comentarios de Adam Thomas sobre no incluir referencias de modelo directamente dentro de una directiva (que también creo que es el mejor enfoque), he actualizado mi jsFiddle para proporcionar un método que no se base en esto.
La directiva utiliza el enlace bidireccional de ámbito local para el scope principal. Los cambios realizados en las variables dentro de la directiva se reflejarán en el scope principal, y viceversa.
HTML:
Código angular:
var app = angular.module('myapp', []); app.controller('Ctrl', function($scope) { $scope.wks = {number: 1, name: 'testing'}; }); app.directive('numberOnlyInput', function () { return { restrict: 'EA', template: '', 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; } }); } }; });
Escribí un ejemplo de CodePen en funcionamiento para demostrar una excelente forma de filtrar las entradas numéricas de los usuarios. La directiva actualmente solo permite números enteros positivos, pero la expresión regular se puede actualizar fácilmente para admitir cualquier formato numérico deseado.
Mi directiva es fácil de usar:
La directiva es muy fácil de entender:
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(); } }); } }; });
Quiero enfatizar que es importante mantener las referencias modelo fuera de la directiva.
Espero que encuentres esto útil.
Muchas gracias a Sean Christe y Chris Grimes por presentarme al ngModelController
Antes que nada, muchas gracias a Adam Thomas . Usé la misma lógica de Adam para esto con una pequeña modificación para aceptar los valores decimales.
Nota: Esto permitirá dígitos con solo 2 valores decimales
Aquí está mi ejemplo de trabajo
HTML
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(); } }); } }; });
Use la etiqueta de paso para establecer el valor mínimo modificable a algún número decimal:
por ejemplo, paso = “0.01”
Hay algo de documentación aquí:
http://blog.isotoma.com/2012/03/html5-input-typenumber-and-decimalsfloats-in-chrome/
DEMO – – jsFiddle
Directiva
.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
Nota: No olvide incluir jQuery con js angulares
Hay una directiva de número de entrada que creo que puede hacer exactamente lo que quiera.
el documento oficial está aquí: http://docs.angularjs.org/api/ng.directive:input.number
Usted podría usar fácilmente el patrón ng.
ng-pattern="/^[1-9][0-9]{0,2}(?:,?[0-9]{3}){0,3}(?:\.[0-9]{1,2})?$/"
HTML
// Simplemente escribe 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); } };
// tipo: 123 o 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); } };
Quería una directiva que pudiera estar limitada en rango por los atributos min
y max
así:
entonces escribí lo siguiente:
.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; }); } }; });
Aquí está mi realmente rápido y sucio:
// Javascript file var app = angular.module('num', ['ngResource']); app.controller('numCtrl', function($scope, $http){ $scope.digits = {}; });
Esto requiere que incluya la biblioteca de recursos angulares para enlaces permanentes a los campos con fines de validación.
Ejemplo de trabajo aquí
Funciona como un campeón en 1.2.0-rc.3 +. Modifique la expresión regular y debería estar todo listo. Tal vez algo así como /^(\d|\.)+$/
? Como siempre, valide el lado del servidor cuando haya terminado.
Este me parece el más fácil: http://jsfiddle.net/thomporter/DwKZh/
(El código no es mío, accidentalmente lo encontré)
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; }); } }; });
Modifiqué la respuesta de Alan anterior para restringir el número al mínimo / máximo especificado. Si ingresa un número fuera del rango, establecerá el valor mínimo o máximo después de 1500 ms. Si borra el campo por completo, no establecerá nada.
HTML:
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'); }); } }; });
Tuve un problema similar y actualicé el ejemplo de input[type="number"]
en documentos angulares para trabajos con precisión de decimales y estoy usando este enfoque para resolverlo.
PD: Un recordatorio rápido es que los navegadores admiten los caracteres “e” y “E” en la entrada [type = “number”], porque se requiere el evento de 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; }); } }; });
Y en tu html puedes usar este enfoque
Aquí está el plunker con este fragmento
Ampliando desde la respuesta de gordy:
Buen trabajo por cierto. Pero también permitió + en el frente. Esto lo eliminará.
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; } } });
Aquí hay una derivada que también bloqueará el punto decimal para ser ingresado dos veces
HTML
Angular
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(/[az!@#$%^&*()_+\-=\[\]{};':"\\|,<>\/?]/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; }); } }; });
Al ampliar la respuesta de Adam Thomas, puede hacer que esta directiva sea más genérica añadiendo argumentos de entrada con expresiones regulares personalizadas:
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
En vivo en CodePen
Por favor revisa mi componente que te ayudará a permitir solo un tipo de datos en particular. Actualmente admite números enteros, decimales, cadenas y horas (HH: MM).
string
– Cadena está permitida con longitud máxima opcional integer
– Entero solo permitido con valor máximo opcional decimal
– Solo se permite el decimal con decimales opcionales y valor máximo (por defecto 2 decimales) time
– 24 hr Formato de hora (HH: MM) solo permitido https://github.com/ksnimmy/txDataType
Espero que ayude.
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); } }; });
DÍGITOS
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); } }; });
directivas angulares para validar números