Angular Data Binding - Input type="number"

37,682

Solution 1

You're right, it has to do with string vs number types. I used a $scope.watch statement to fix it: http://jsfiddle.net/ZvdXp/6/

Solution 2

You can also fix this with a directive. I created a directive to force input bound to numeric fields to be numeric.

Html:

myApp.directive('numericbinding', function () {
        return {
            restrict: 'A',
            require: 'ngModel',
            scope: {
                model: '=ngModel',
            },                
           link: function (scope, element, attrs, ngModelCtrl) {
               if (scope.model && typeof scope.model == 'string') {
                   scope.model = parseInt(scope.model);
               }                  
            }
        };
});

You can add it to your numeric field like this:

<input data-ng-model="stringnumber" numericbinding type="number"/>    

full example: http://jsfiddle.net/tdjager/cMYQ3/1/

Solution 3

I've expanded on Tim's answer to make it correct the data type after the user updates the control value as well.

myApp.directive('numericbinding', function () {
    return {
        restrict: 'A',
        require: 'ngModel',
        scope: {
            model: '=ngModel',
        },                
        link: function (scope, element, attrs, ngModelCtrl) {
           if (scope.model && typeof scope.model == 'string') {
               scope.model = parseInt(scope.model);
           } 
           scope.$watch('model', function(val, old) {
               if (typeof val == 'string') {
                   scope.model = parseInt(val);
               }
           });                 
        }
    };
});

Solution 4

If you prefer to save a numeric value in the model, you can use a directive that convert the string generated by the text input and by the range input in a numeric value through the angular parser, like so:

myApp.directive('numericsaving', function () {
    return {
        restrict: 'A',
        require: '?ngModel',
        scope: {
            model: '=ngModel'
        },
        link: function (scope, element, attrs, ngModelCtrl) {
            if (!ngModelCtrl) {
                return;
            }
            ngModelCtrl.$parsers.push(function (value) {
                if (!value || value==='' || isNaN(parseInt(value)) || parseInt(value)!==value) {
                    value=0;
                }
                return parseInt(value);
            });
        }
    };
});

In the HTML, leave the number input as is and add the directive in the others inputs this way:

<input type="number" min="0" max="50" value="{{value}}" ng-model="value" />
<input type="range" min="0" max="50" value="{{value}}" ng-model="value" numericsaving/>
<input type="text" value="{{value}}" ng-model="value" numericsaving/>

The angular parser will translate the string input in a numeric value before saving it in model, so the numeric input will automatically work. Here the complete fiddle.

Moreover, if the user inserts letters or any strange character in the text input, they will not be saved in the model, preventing invalid value in the single source of truth of your application. Only '+' and '-' character at the begin of the text will be correctly parsed, so even negative value are allowed. I hope this helps! :)

Solution 5

TypeScript version inspired ainos984, for the sake of posterity

     export class ngIntegerDirective implements ng.IDirective {

        static directiveKey: string = 'ngInteger';

        require: string = 'ngModel';

        link = (scope, ele, attr, ctrl: ng.INgModelController) => {
            ctrl.$parsers.unshift(function (viewValue) {
                let result: number = parseInt(viewValue,10);
                if (isNaN(result)) {
                    result = 0;
                }
                return result;
            });
        }

        public static Factory(): ng.IDirectiveFactory {
            const directive = () => new ngIntegerDirective();
            directive.$inject = []; //injecter les dépendances ici
            return directive;
        }
    }
Share:
37,682
Admin
Author by

Admin

Updated on July 16, 2022

Comments

  • Admin
    Admin almost 2 years

    I'm having problems binding a number value using AngularJS.

    I've put a simplified example on JSFiddle: http://jsfiddle.net/treerock/ZvdXp/

    <div ng-controller="MyCont" ng-app>  
        <input type="number" min="0" max="50" value="{{value}}" ng-model="value" />    
        <input type="text" value="{{value}}" ng-model="value" />
        <input type="range" min="0" max="50" value="{{value}}" ng-model="value" />    
        {{value}}   
    </div>
    

    This should be three different types of input fields, and if you update one, then all values should update. That's working except for the number input. e.g. If I type 20 in the first number box, it updates all other instances of value. But if I update the text or range inputs, the number input goes blank.

    I was wondering if the issue was with how the number is represented/converted between fields. e.g. the number input is a float and the text input is a string?

  • rweng
    rweng about 10 years
    This only works once during the linking phase, as far as I see it
  • Peter Butkovic
    Peter Butkovic over 9 years
    @rweng any chance to update it in a way it would work in a $watch way? I like the idea of directive, so'd like to apply it here. Still my angular know how is rather poor.
  • Peter Butkovic
    Peter Butkovic over 9 years
    OK, figured it myself, see: stackoverflow.com/questions/27237827/…
  • Souhaieb Besbes
    Souhaieb Besbes about 8 years
    ca you please provide a typescript version? i have a problem with $parsers not being recognized
  • Souhaieb Besbes
    Souhaieb Besbes about 8 years
    parseInt(value)!==value this condition will always be false since the value is a string and the parsed value is a number, you can change it to != (like the fiddle) but then it will be useless
  • ainos984
    ainos984 about 8 years
    You are right Souhaieb, the parseInt(value)!==value will always be false, the correct version is parseInt(value)!=value. This check isn't absolutely useless, it prevents string starting with a number, for example: var s="25"; parseInt(value)==value ; //return true ----- var s="25abc"; parseInt(value)==value ; //return false
  • dma_k
    dma_k about 7 years
    The check is perhaps not useless, but indeed !== always results to false, because parsed value is number. @ainos984: You latest comment shows your idea, but is not completely correct: you need to say var s="25"; parseInt(value)===value and it will be always false.