Getter & setter support with ng-model in AngularJs

sboisse picture sboisse · Jan 22, 2014 · Viewed 15.4k times · Source

I am trying to get getter/setter support for ng-model by implementing a directive that will take care of getting and setting the values to/from the view/model. I am almost there, but I end up in infinite $digest loops.

The idea is to set ng-model="$someFieldToStoreInTheScope", and then have the getter/setter directive do the updates between that field and the getter/setter functions.

I use $watch to update the model using the setter expression when the ngModelController updates the field in the scope, and another watch to update that field when the getter expression changes.

Have a look at: http://jsfiddle.net/BDyAs/10/

Html:

<div ng-app="myApp">
<body>
<form name="form">    
    <input type="text" ng-model="$ngModelValue" ng-model-getter-setter="get=getValue();set=setValue($value)"/> {{myDerivedValue}}
</form>
</body>
</div>

JS:

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

myApp.directive(
    {
        'ngModelGetterSetter': function () {
            return {
                require: "ngModel",
                controller: ctrl,
                link:  function(scope, element, attrs, ngModelCtrl)
                {
                    var getterSetterExpression = attrs.ngModelGetterSetter;
                    var tokens = getterSetterExpression.split(";");
                    var getExpression = tokens[0].split("=")[1];
                    var setExpression = tokens[1].split("=")[1];

                    function updateViewValue()
                    {
                        var updateExpression = attrs.ngModel + "=" + getExpression;
                        scope.$eval(updateExpression);
                    }

                    function updateModelValue()
                    {
                        scope.$value = ngModelCtrl.$viewValue;
                        scope.$eval(setExpression);
                    }

                    updateViewValue();    

                    scope.$watch(getExpression, updateViewValue);
                    scope.$watch(attrs.ngModel, updateModelValue);
                }
            };
        }
    });

function ctrl($scope) {
    $scope.getValue = function ()
    {
        return $scope.myValue;
    }

    $scope.setValue = function (val)
    {
        $scope.myValue = val;
        $scope.myDerivedValue = $scope.myValue * 2;
    }

    $scope.setValue(5);

    setInterval(function () { $scope.setValue($scope.getValue() + 1); $scope.$apply(); }, 1000);
}

I have put a setInterval() in my code to modify the model and see if it propagates correctly in the view.

Any idea why there is an infinite digest loop, and how to remove it?

Answer

sboisse picture sboisse · Jan 22, 2014

NOTE AngularJs 1.3 now supports Getter/Setter for ng-model. Refer to http://docs.angularjs.org/api/ng/directive/ngModelOptions for more information.


I could break the infinite loop with extra calls to

ngModelCtrl.$setViewValue()

and

ngModelCtrl.$render()

in the event handlers. Not sure if it's the best way to do it though.

See fiddle: http://jsfiddle.net/BDyAs/12/

EDIT:

I improved the code even more in

http://jsfiddle.net/BDyAs/15/

by separating the directive in separate ones for the getter and the setter.