Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

AngularJS version 1.3.13 throws an error Error: [ngModel:numfmt] Expected 0.00 to be a number #11157

Closed
NaomiN opened this issue Feb 24, 2015 · 26 comments

Comments

@NaomiN
Copy link

NaomiN commented Feb 24, 2015

I am getting the following error:

 Error: [ngModel:numfmt] Expected `0.00` to be a number
http://errors.angularjs.org/1.3.13/ngModel/numfmt?p0=0.00
    at REGEX_STRING_REGEXP (angular.js:63)
    at Array.<anonymous> (angular.js:19884)
    at Object.ngModelWatch (angular.js:23289)
    at Scope.$get.Scope.$digest (angular.js:14235)
    at Scope.scopePrototype.$digest (hint.js:1468)
    at Scope.$get.Scope.$apply (angular.js:14506)
    at Scope.scopePrototype.$apply (hint.js:1478)
    at done (angular.js:9659)
    at completeRequest (angular.js:9849)
    at XMLHttpRequest.requestLoaded (angular.js:9790)

However, 0.00 is a number, so why would I see such an error?

The object generating this error is this:

  <input type="number" name="creditLimit"
                           id="creditLimit"
                           ng-model="currentAccount.crLimit"
                           class="form-control"
                           min="0"
                           placeholder="1500.00"
                           data-sm:number data-sm:number-format data-accuracy="2" />
@NaomiN
Copy link
Author

NaomiN commented Feb 24, 2015

If I remove the last line for the custom directive smNumber, I don't get that error.

@matsko
Copy link
Contributor

matsko commented Feb 24, 2015

Could you please provide a working example of your code using plunkr? http://plnkr.co/

@matsko matsko added this to the Backlog milestone Feb 24, 2015
@NaomiN
Copy link
Author

NaomiN commented Feb 24, 2015

It's a bit tricky as this is part of a big complex project, but I'll try later on. I've traced the code and the value was coming as "0.00" (e.g. string) and the first check for not empty was returning false and then the second check for isNumber was returning false as well (perhaps because the value was passed as a string).

@NaomiN
Copy link
Author

NaomiN commented Feb 24, 2015

Hi Matias,

The failing code is this:

ctrl.$formatters.push(function(value) {
if (!ctrl.$isEmpty(value)) {
if (!isNumber(value)) {
throw $ngModelMinErr('numfmt', 'Expected {0} to be a number',
value);
}
value = value.toString();
}
return value;
});

The isNumber function checks the typeof value and not if that value is a
number or not.

In our case we use the following code in HTML:

@Labels.percentage:
ng-model="currentSpecial.discPct" type="number"
data-sm:number
data-sm:number-format data-accuracy="0">
%


@String.Format(Messages.mustBeBetweenXandY, Labels.percentage, "-100",
"100")



and I attach the smNumber and smNumberFormat directives to this email. I've
tested with input type="text" and there is no error, but then min/max
properties are not respected.

What would you suggest for the fix here?

Thanks in advance.

On Tue, Feb 24, 2015 at 12:31 AM, Matias Niemelä notifications@github.com
wrote:

Could you please provide a working example of your code using plunkr?
http://plnkr.co/


Reply to this email directly or view it on GitHub
#11157 (comment)
.

@NaomiN
Copy link
Author

NaomiN commented Feb 24, 2015

So this is the current angularjs implementation of the number format:

function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
badInputChecker(scope, element, attr, ctrl);
baseInputType(scope, element, attr, ctrl, $sniffer, $browser);

ctrl.$$parserName = 'number';
ctrl.$parsers.push(function(value) {
if (ctrl.$isEmpty(value)) return null;
if (NUMBER_REGEXP.test(value)) return parseFloat(value);
return undefined;
});

ctrl.$formatters.push(function(value) {
if (!ctrl.$isEmpty(value)) {
if (!isNumber(value)) {
throw $ngModelMinErr('numfmt', 'Expected {0} to be a number',
value);
}
value = value.toString();
}
return value;
});

if (attr.min || attr.ngMin) {
var minVal;
ctrl.$validators.min = function(value) {
return ctrl.$isEmpty(value) || isUndefined(minVal) || value >= minVal;
};

attr.$observe('min', function(val) {
  if (isDefined(val) && !isNumber(val)) {
    val = parseFloat(val, 10);
  }
  minVal = isNumber(val) && !isNaN(val) ? val : undefined;
  // TODO(matsko): implement validateLater to reduce number of

validations
ctrl.$validate();
});
}

if (attr.max || attr.ngMax) {
var maxVal;
ctrl.$validators.max = function(value) {
return ctrl.$isEmpty(value) || isUndefined(maxVal) || value <= maxVal;
};

attr.$observe('max', function(val) {
  if (isDefined(val) && !isNumber(val)) {
    val = parseFloat(val, 10);
  }
  maxVal = isNumber(val) && !isNaN(val) ? val : undefined;
  // TODO(matsko): implement validateLater to reduce number of

validations
ctrl.$validate();
});
}
}

I am guessing something has changed since the version 1.10.2 we were using
before. My question is - it looks like we can not use input type="number"
and our 2 directives at the same time. However, if we're using input
type="text" and our directives, then the min/max attributes are not
observed (obviously).

So, what is the best course of actions for us to be able to format the
number with commas and predefined number of decimals (called accuracy in
our case) and also have min/max attributes to operate? Since our directive
already attempts to format the value it's coming as a string to that
function and then the isNumber check is failing.

Thanks in advance.

I am thinking I may try to add lower priority to our formatting directive
to ensure correct behavior.

UPDATE. Adding priority: - 3 to the top of our smNumberFormat directive didn't change the outcome, I still get the same error.

On Tue, Feb 24, 2015 at 1:39 PM, Naomi Nosonovsky nnosonovsky@gmail.com
wrote:

Hi Matias,

The failing code is this:

ctrl.$formatters.push(function(value) {
if (!ctrl.$isEmpty(value)) {
if (!isNumber(value)) {
throw $ngModelMinErr('numfmt', 'Expected {0} to be a number',
value);
}
value = value.toString();
}
return value;
});

The isNumber function checks the typeof value and not if that value is a
number or not.

In our case we use the following code in HTML:

@Labels.percentage:
ng-model="currentSpecial.discPct" type="number"
data-sm:number
data-sm:number-format data-accuracy="0">
%


@String.Format(Messages.mustBeBetweenXandY, Labels.percentage, "-100",
"100")



and I attach the smNumber and smNumberFormat directives to this email.
I've tested with input type="text" and there is no error, but then min/max
properties are not respected.

What would you suggest for the fix here?

Thanks in advance.

On Tue, Feb 24, 2015 at 12:31 AM, Matias Niemelä <notifications@github.com

wrote:

Could you please provide a working example of your code using plunkr?
http://plnkr.co/


Reply to this email directly or view it on GitHub
#11157 (comment)
.

@Narretz
Copy link
Contributor

Narretz commented Feb 26, 2015

Can you please put a number input with your custom directives into a plnkr? The framework code is not particularly helpful, I'm afraid.

@Narretz Narretz modified the milestones: Purgatory, Backlog Feb 26, 2015
@andr3medeiros
Copy link

I am having same issue with an customized angular touchspin directive (https://gist.github.com/jdelibas/b20160fced5963a338ab#file-spindirective-js) of jquery's touchspin plugin (http://www.virtuosoft.eu/code/bootstrap-touchspin):

'use strict';

angular.module('app')
    .directive('spin', function() {
        return {
            templateUrl: 'components/templates/touchspin.html',
            scope: {
                bindModel:'=ngModel',
                change   :'&ngChange'
            },
            restrict: 'E',
            require: 'ngModel',
            bindAttr: '=',
            link: function(scope, element, attrs) {
                var min,max,step,input,initial, decimals, prefix, verticalbuttons, stepinterval;
                element = angular.element(element);

                if(typeof attrs === 'undefined')throw new Error('Spin.js attributes missing');

                min = typeof attrs.min !== 'undefined' ? parseFloat(attrs.min) : 1;
                max = typeof attrs.max !== 'undefined' ? parseFloat(attrs.max) : 999;
                step = typeof attrs.step !== 'undefined' ? parseFloat(attrs.step) : 1;
                prefix = typeof attrs.prefix !== 'undefined' ? attrs.prefix : '';
                decimals =  typeof attrs.decimals !== 'undefined' ? parseInt(attrs.decimals) : 0;

                input = $('input[name="spin"]',element);

                input.TouchSpin({
                    min: min,
                    max: max,
                    step: step,
                    decimals: decimals,
                    prefix: prefix,
                    forcestepdivisibility : 'none',
                    booster : false
                });

                input.on('change', function(e){
                    scope.value = parseFloat(input.val());

                    if(scope.change) {
                        scope.change(scope.value);
                    }

                    if(!scope.$$phase) {
                        scope.$apply();
                    }

                });

            }
        };
    });

"[ngModel:numfmt] Expected 0.02 to be a number
http://errors.angularjs.org/1.3.0/ngModel/numfmt?p0=0.02"

I tried put that function into $apply too:

scope.$apply(function() {
if(scope.change) {
                        scope.change(scope.value);
                    }
});

@NaomiN
Copy link
Author

NaomiN commented Mar 2, 2015

I'm getting this error now: Error: [ngModel:numfmt] Expected {"$viewValue":null,"$modelValue":null,"$validators":{},"$asyncValidators":{},"$parsers":[null,null],"$formatters":[null],"$viewChangeListeners":[],"$untouched":true,"$touched":false,"$pristine":true,"$dirty":false,"$valid":true,"$invalid":false,"$error":{},"$name":"payment","$options":null} to be a number

For the following markup:

@Labels.currencySymbol
@Messages.onlyPositivePayments
@Labels.pageTotal {{total('invoiceBalance')|currency}} {{total('payment')|currency}}

So, input type="number" doesn't seem to be working in the table created using ng-repeat directive.

@NaomiN
Copy link
Author

NaomiN commented Mar 2, 2015

I'll try to reply using e-mail: I'm getting an error using this html:

@*{{result.invoice_No}}*@ {{ result.invoice_No }} {{result.descrip}} {{result.dateCreated |date: 'medium'}} {{result.invoiceBalance | currency}}
@Labels.currencySymbol
@Messages.onlyPositivePayments
@Labels.pageTotal {{total('invoiceBalance')|currency}} {{total('payment')|currency}}

It seems like input type="number" is not working inside the ng-repeat
directive.

On Fri, Feb 27, 2015 at 8:44 AM, André da Silva Medeiros <
notifications@github.com> wrote:

I am having same issue with an customized angular touchspin directive (
https://gist.github.com/jdelibas/b20160fced5963a338ab#file-spindirective-js)
of jquery's touchspin plugin (
http://www.virtuosoft.eu/code/bootstrap-touchspin):

'use strict';

angular.module('app')
.directive('spin', function() {
return {
templateUrl: 'components/templates/touchspin.html',
scope: {
bindModel:'=ngModel',
change :'&ngChange'
},
restrict: 'E',
require: 'ngModel',
bindAttr: '=',
link: function(scope, element, attrs) {
var min,max,step,input,initial, decimals, prefix, verticalbuttons, stepinterval;
element = angular.element(element);

            if(typeof attrs === 'undefined')throw new Error('Spin.js attributes missing');

            min = typeof attrs.min !== 'undefined' ? parseFloat(attrs.min) : 1;
            max = typeof attrs.max !== 'undefined' ? parseFloat(attrs.max) : 999;
            step = typeof attrs.step !== 'undefined' ? parseFloat(attrs.step) : 1;
            prefix = typeof attrs.prefix !== 'undefined' ? attrs.prefix : '';
            decimals =  typeof attrs.decimals !== 'undefined' ? parseInt(attrs.decimals) : 0;

            input = $('input[name="spin"]',element);

            input.TouchSpin({
                min: min,
                max: max,
                step: step,
                decimals: decimals,
                prefix: prefix,
                forcestepdivisibility : 'none',
                booster : false
            });

            input.on('change', function(e){
                scope.value = parseFloat(input.val());

                if(scope.change) {
                    scope.change(scope.value);
                }

                if(!scope.$$phase) {
                    scope.$apply();
                }

            });

        }
    };
});

"[ngModel:numfmt] Expected 0.02 to be a number
http://errors.angularjs.org/1.3.0/ngModel/numfmt?p0=0.02"

I tried put that function into $apply too:

scope.$apply(function() {if(scope.change) {
scope.change(scope.value);
}
});


Reply to this email directly or view it on GitHub
#11157 (comment)
.

@Narretz
Copy link
Contributor

Narretz commented Mar 2, 2015

Again, please provide a minimal reproducible example, either in a plnkr.co in a gist or even copy/ pasted here. Your snippets so far are impossible to debug.

@NaomiN
Copy link
Author

NaomiN commented Mar 2, 2015

Are you able to create a table with ng-repeat directive and put a number
(input type="number") into one cell? Does it work OK for you?

Can you please start that plnkr and then I'll modify to add extra if needed?

On Mon, Mar 2, 2015 at 4:21 PM, Martin Staffa notifications@github.com
wrote:

Again, please provide a minimal reproducible example, eithe in a plnkr.co
in a gist or even copy/ pasted here. Your snippets so far are impossible to
debug.


Reply to this email directly or view it on GitHub
#11157 (comment)
.

@Narretz
Copy link
Contributor

Narretz commented Mar 2, 2015

You should be able to take it from here. Plnkr is pretty self-explanatory.
http://plnkr.co/edit/0qNfnDCbnBKnPvgkV6XC?p=preview

@NaomiN
Copy link
Author

NaomiN commented Mar 2, 2015

Here is what I've done but I didn't reproduce the error.

http://plnkr.co/edit/HBmOKTqQ9p4m3PzHNvds?p=preview

On Mon, Mar 2, 2015 at 5:02 PM, Martin Staffa notifications@github.com
wrote:

You should be able to take it from here. Plnkr is pretty self-explanatory.
http://plnkr.co/edit/0qNfnDCbnBKnPvgkV6XC?p=preview


Reply to this email directly or view it on GitHub
#11157 (comment)
.

@jonagoldman
Copy link

Having the same problem here (with 1.3.13), but again this is happening inside a large project and I'm not able to reproduce.

Error: [ngModel:numfmt] Expected `{"$viewValue":null,"$modelValue":"<<already seen>>","$validators":{},"$asyncValidators":{},"$parsers":[null,null],"$formatters":[null],"$viewChangeListeners":[],"$untouched":true,"$touched":false,"$pristine":true,"$dirty":false,"$valid":true,"$invalid":false,"$error":{},"$name":"profitLevel","$options":null}` to be a number

@wesleycho
Copy link
Contributor

Can you add your directive code to that plunker @NaomiN ?

@la40
Copy link

la40 commented Mar 13, 2015

There is a bug in numberInputType function of Angularjs. I use 1.3.6 and i have checked it in 1.3.14, same there. This part of code convert data from model format to view format when input type is number.

ctrl.$formatters.push(function(value) {
if (!ctrl.$isEmpty(value)) {
/if (!isNumber(value)) {
throw $ngModelMinErr('numfmt', 'Expected {0} to be a number', value);
}
/
value = value.toString();
}
return value;
});

The checking if value is number and throwing error here make no sense at all. After that the value is cast to string :)

Just comment it and everithing will work fine. It's work for me ;)

@jamjamg
Copy link

jamjamg commented Mar 16, 2015

What about replacing "(!isNumber(value))" with "(isNaN( parseFloat( value ) ) || !isFinite( value ))" ?
( just like the "jQuery.isNumeric()" )

jamjamg pushed a commit to jamjamg/angular.js that referenced this issue Mar 16, 2015
@petebacondarwin
Copy link
Member

OK, so the problem you are having is that <input type="number" ng-model="x"> expects x to be a real number and not a string that looks like a number.

You can work around this by adding a formatting directive to parse the string before it gets to the input[number] directive. See http://plnkr.co/edit/Zf0qcsPQeyaUU5l0MZhD?p=preview

The question is whether the input[number] directive should be more lenient and just try to parse the number. In any case, the directive will be parsing the $viewValue of the input element into a real number when the input changes, meaning that the $modelValue will become a real number at that point. This would lead to inconsistency in your model, with the model value changing from a string to a number at some point.

I would argue that this inconsistency is not desirable and a more consistent and reliable approach is to use the second directive (as shown in the plunker above) to do the conversion, allowing and ensuring your model stays consistently as a string.

petebacondarwin added a commit that referenced this issue Mar 19, 2015
The docs also now link through to the error doc, which contains a runnable
example of how to work around this restriction.

Closes #11157
Closes #11334
@petebacondarwin
Copy link
Member

I updated the docs (both the input[number] directive and numfmt error) with better information and a runnable example about to deal with this situation.

petebacondarwin added a commit that referenced this issue Mar 19, 2015
The docs also now link through to the error doc, which contains a runnable
example of how to work around this restriction.

Closes #11157
Closes #11334
@NaomiN
Copy link
Author

NaomiN commented Mar 19, 2015

Here is my plnkr with our directives demonstrating the issue

http://plnkr.co/edit/z1BWMoPmnsCZlAUra0os?p=preview

@petebacondarwin
Copy link
Member

@NaomiN - I don't see the error message in your Plunker.

@nicolaes
Copy link

nicolaes commented Apr 7, 2015

You can see the bug reproducing here http://plnkr.co/edit/qKC6YESYLbdDLe6bvNRM?p=preview
Practically you can not assign a stringified number to a <input type="number" />

This is especially annoying when you use strings to deal with floating point errors, and Angular doesn't let you mitigate the issue.

+1 for @jamjamg 's solution #issuecomment-81638606

@petebacondarwin
Copy link
Member

input[number] directives expect the model to be a number. If you are not passing a number to it then you must convert it first, which you can do with a directive. See this plunker: http://plnkr.co/edit/ZgewoGxx46LLDOieaso6?p=preview

@nicolaes
Copy link

Hey @petebacondarwin ,
I know you can use parseFloat(), but there are times you need better than the 64-bit precision of JS float.
You will then be forced to use strings only (e.g. BigDecimal in JS) and inevitably break Angular when using <input type="number" /> .

Regards,
Nick

@Bebokkk
Copy link

Bebokkk commented Jul 2, 2015

I had same issue, its sorted in latest version of angular, update angular to latest.

@petebacondarwin
Copy link
Member

@Bebokkk - as a workaround you can simply remove the parsers and formatters that were added by AngularJS's numberInputType directive: http://plnkr.co/edit/rtOVrx6k1KAJ1WyC96tn?p=preview

netman92 pushed a commit to netman92/angular.js that referenced this issue Aug 8, 2015
The docs also now link through to the error doc, which contains a runnable
example of how to work around this restriction.

Closes angular#11157
Closes angular#11334
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests