4

Yesterday, I am having trouble on how to make a formatted number in my number fields but now that I achieve it, I am having problem on how to do the calculation.

index.html

<div ng-controller="MyCtrl">
    <input ng-model="var.value" class="integers" symbol="°" />
    <br /><br />
    {{var.value * 100}}
</div>

app.js

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

angular.module('ui.numeric', []).directive('integers', function() {
return {
    require: 'ngModel',
    restrict: 'EACM',
    link: function(scope, element, attrs, modelCtrl) {
        scope.$watch(element, function() {
            var formatted_number = accounting.formatMoney(element.val(),"", 2,",",".","%s%v");
            modelCtrl.$setViewValue(formatted_number);
            modelCtrl.$render();
        });
        element.bind('blur', function() {

            var formatted_number = accounting.formatMoney(element.val(),"", 2,",",".","%s%v");
            modelCtrl.$setViewValue(formatted_number);
            modelCtrl.$render();
            scope.$apply();
        });
        element.bind('focus', function() {
            var plainNumber = accounting.unformat(element.val())
            if(plainNumber == "0") plainNumber = "";
            modelCtrl.$setViewValue(plainNumber);
            modelCtrl.$render();
            scope.$apply();
            console.log("I am focused!")
        });
    }
};
});

function MyCtrl($scope) {
    $scope.var = {"value":1000.36};
}

It works when the value of the field is less than a thousand but when I reach 1000, the number would be formatted with comma 1,000 and it becomes string and can't do anymore calculations.

All I want is to properly format my number fields(for display) but retain the true value to do calculations without using separate variable in each number field.

Please see the fiddle. Thanks!

http://jsfiddle.net/aldesabido/1syh7cne/6/

updated fiddle: http://jsfiddle.net/aldesabido/1syh7cne/14/

Solution: changed the modelCtrl.setViewValue(number) to modelCtrl.viewValue(number) so that the display only will be affected not the actual value (not sure about this workaround though.).

http://jsfiddle.net/aldesabido/1syh7cne/18/

Thanks for the help guys!

aldesabido
  • 1,268
  • 2
  • 17
  • 38

2 Answers2

2

The only solution I can think of is to watch for changes in {{ var.value }} on the controller:

function MyCtrl($scope) {
    $scope.var = {"value":1000.36};

    $scope.$watch('var.value', function(val) {
        $scope.numbericVal = accounting.unformat(val);
    });
}

And then, use numericVal in your view like this: {{numbericVal * 2}}

Example: http://jsfiddle.net/1syh7cne/7/


Another RAW example that using callback function whenever the value gets changed: http://jsfiddle.net/1syh7cne/9/

I've used the attributes to define a function that pass the object and the current numeric value. Note that you can just parse the val of the passed object instead of trusting the second parameter in the function.

var invoker = $parse(attrs.updateFunc);
invoker(scope, {number: plainNumber});

Just take a look - I think you'll see what I did there. But as I said - It's a general direction for a possible solution.

Alon Eitan
  • 11,997
  • 8
  • 49
  • 58
  • Thanks for the response. But I don't really want to use two seperate variable for displaying and calculation. I hope that there's still a way for this. – aldesabido Jul 03 '16 at 04:23
  • I did my best, couldn't think of anything else. Hope you'll find a solution using one variable (That would be an interesting one) – Alon Eitan Jul 03 '16 at 04:24
  • Thanks for the effort bro. It would really a pain in the ass to do this in all of my number fields. – aldesabido Jul 03 '16 at 04:48
  • @aldesabido I have just posted another example, so take a look and let me know. I'm off to bed.... – Alon Eitan Jul 03 '16 at 06:02
  • @aldesabido Thanks for that. I prepared a last example that demonstrate why the solution in your answer is not perfect - http://jsfiddle.net/1syh7cne/16/ Just by injecting `$interval` to the controller and putting some mock action (`console.log(....)`) causing Angular to trigger a digest cycle and update the view. So removing the `$apply()` is just delaying the next digest cycle - giving you a **false** feeling that everything is working fine – Alon Eitan Jul 03 '16 at 12:37
  • oh I see it now. need more testing then. I hope that you could help me again bro. – aldesabido Jul 04 '16 at 05:42
  • Can't really understand what's the $digest error is. – aldesabido Jul 04 '16 at 06:39
  • Where exactly you see a `$digest` error? I don't see any in mine (http://jsfiddle.net/1syh7cne/9/). Can you elaborate please? – Alon Eitan Jul 04 '16 at 07:01
  • This is working great! I'll have a deeper look to see what you did, because I find it interesting. Thanks – Alon Eitan Jul 04 '16 at 11:24
  • BTW - There's also this module for input masking http://assisrafael.github.io/angular-input-masks/ (Although it looks like you managed to debug your directive and it's working just fine) – Alon Eitan Jul 04 '16 at 11:33
  • I tried that before but I think it doesn't fit on my specific need. Thanks for everythin @Alon. – aldesabido Jul 05 '16 at 01:03
  • Is it okay if I add you on my contact list? – aldesabido Jul 05 '16 at 01:04
  • I discovered another problem with my solution. It won't work if I use "+" instead it will concatenate two numbers(treat as string) – aldesabido Jul 05 '16 at 06:49
  • What you can do is `{{ firstStringNum * 1 + secondStringNum * 1 }}` - It usually works for me – Alon Eitan Jul 05 '16 at 07:02
  • 1
    haha. yah that would work but I really don't want to do it in all of my module. but gonna use it as my trump card. – aldesabido Jul 05 '16 at 07:04
  • no choice left. need to parseFloat all of it before calculation. :( – aldesabido Jul 05 '16 at 08:35
  • Can't you create a custom filter for that? (http://stackoverflow.com/questions/26293769/how-to-parseint-in-angular-js) – Alon Eitan Jul 05 '16 at 08:41
  • Some of my calculations are not in the template that's why I can't do that. – aldesabido Jul 05 '16 at 08:55
1

If you are not limited to using only Angular you can check out accounting.js library which does exactly what you need.

Then you could also keep a variable on $scope, that you use for calculation only, and you maintain the state between your var.value and that other $scope variable with help of accounting.js for formatting.

I hope this helps. Good luck.

pzelenovic
  • 420
  • 3
  • 15
  • actually I am using accounting.js for my formatting. I don't really want to use two seperate variables for the formatted one and the original value. – aldesabido Jul 03 '16 at 04:21