The scope.$watch item doens't work for fields of javascript objects

551 views Asked by At

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?

2

There are 2 answers

0
daremachine On

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

5
Andyrooger On

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