0

I faced with problem of watching properties of java script objects. Partially I described this problem here, but I didn't get solution...

I will describe the problem another way. Here is fiddle

Here is code of my directive:

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

var ctrl = function($scope) {
        $scope.amount = '0.00';

        $scope.values = {
                amount: 0.00
        };
};

myApp.directive('currency', function($filter) {
    return {
        restrict: "A",
        require: "ngModel",
        scope: {
            separator: "=",
            fractionSize: "=",
            ngModel: "="
        },
        link: function(scope, element, attrs) {

            if (typeof attrs.separator === 'undefined' || 
                    attrs.separator === 'point') {
                scope.separator = ".";
            } else {
                scope.separator = ",";
            };

            if (typeof attrs.fractionSize === 'undefined') {
                scope.fractionSize = "2";
            };

            scope[attrs.ngModel] = "0" + scope.separator;
            for(var i = 0; i < scope.fractionSize; i++) {
                scope[attrs.ngModel] += "0";
            };

            scope.$watch(attrs.ngModel, function(newValue, oldValue) {

                if (newValue === oldValue) {
                    return;
                };

                var pattern = /^\s*(\-|\+)?(\d*[\.,])$/;

                if (pattern.test(newValue)) {
                    scope[attrs.ngModel] += "00";
                    return;
                };

            }, true);
        }
    };
});

And this is my HTML layout:

<div ng-app="myApp">
    <div ng-controller="ctrl">
        {{amount}}<br>
        <input type="text" style="text-align: right;" ng-model="amount" currency separator="point" fraction-size="2"></input>
    </div>
</div>

I want to bind the value in my input element to values.amount item in outer controller, but the watch instruction of my directive doesn't work...

The logic I want to implement is the following: I want to add extra zeros to the input element if user put a point. I mean if the value in input element say "42" and user put there a point, so the value now is "42." then two extra zeros have to be aded to be like this "42.00".

If I use ng-model="amount" to bing data of input element to variables in outer controller the logic in input element works, but amount value of outer controller doesn't update.

If I use ng-model="values.amount" for binding, neither values.amount of outer controller nor input element logic works.

In fact I have to bind input element to values.amount using ng-model="values.amount" instruction, but it doesn't work and I don't know why.

Can somebody help me to fix this problem? Any ideas or suggestions?

Community
  • 1
  • 1
Roman Dryndik
  • 1,167
  • 1
  • 13
  • 34
  • 1
    possible duplicate of [watch changes on JSON object properties](http://stackoverflow.com/questions/20379196/watch-changes-on-json-object-properties) – Stewie Dec 04 '13 at 22:40

2 Answers2

0

This doesn't work because of the expression

scope[attrs.ngModel] = ...

In the amount case, this just assigns to scope.amount. But in the values.amount this does not assign to scope.values.amount, instead it assigns the 'values.amount' property of scope.

Rather than assigning in this way you could use the $parse service like so.

var parsed = $parse(attrs.ngModel); // Parse expression
var currentValue = parsed(scope); // Evaluate expression
parsed.assign(scope, 'newvalue'); // Assign expression

This should fix your problem, however you may want to look at using parsers and formatters for this instead as a nicer approach. Rather than interpreting the value of the model yourself, you can view and update this through the ngModel controller you have required, using parsers and formatters to control conversion.

  • Fiddle with smaller fix
  • Fiddle with parsers and formatters
Andyrooger
  • 6,748
  • 1
  • 43
  • 44
  • 1
    I would use `scope.$eval(attrs.ngModel)` instead of `$parse`! – angabriel Dec 05 '13 at 01:36
  • @angabriel Why? I'm using it mainly for assignment, which $eval doesn't do in this way. But even for evaluation, $eval only calls $parse(express)(scope). Calling it manually means we don't need to do the entire parse each time, we just slot in the new scope vals... – Andyrooger Dec 05 '13 at 09:09
  • @angabriel this works... I mean logic works, but values in controller... They are still without any change. – Roman Dryndik Dec 05 '13 at 11:11
  • @Andyrooger As far as i know, scope.$eval executes on the scope it was called with while $parse is more general. Ok you seem to know what you are doing, i didnt see your line `parsed(scope)` which is then equivalent to `scope.$eval(exp)` :-). – angabriel Dec 05 '13 at 11:14
  • @RomanDryndik I hadn't noticed that bit - it's broken on both inputs (doesn't ever change), though more obvious on `values.amount` because this is set to a number rather than string and so prints as '0'. The problem comes from the isolated scope on your directive causing assignment to happen to the isolated scope rather than the scope your model is in. Either you can remove the isolate scope or use `scope.$parent` in the calls to `$parse`. – Andyrooger Dec 05 '13 at 12:11
0

i tried solve your problem and here is it:

1) remove your directive scope
2) add ngModel into link function
3) remove this part of your code (not needed)

scope[attrs.ngModel] = "0" + scope.separator;
for(var i = 0; i < scope.fractionSize; i++) {
   scope[attrs.ngModel] += "0";
};

and replace $watch instead this

ngModel.$parsers.unshift(function(viewValue) {
   var pattern = /^\s*(\-|\+)?(\d*[\.,])$/;

   if (pattern.test(viewValue)) {
      viewValue += "00";
   };

   element.val(viewValue); // set element view value
   return viewValue; // set new model value
});

i try this and working fine.

jsFiddle demo

resource this post

cheers

Community
  • 1
  • 1
daremachine
  • 2,678
  • 2
  • 23
  • 34