Поддержка Getter & setter с ng-моделью в AngularJs

Я пытаюсь получить поддержку getter/setter для ng-модели, выполнив директиву, которая позаботится о получении и установке значений в/из представления/модели. Я почти там, но в конечном итоге получаю бесконечные $digest петли.

Идея состоит в том, чтобы установить ng-model = "$ someFieldToStoreInTheScope", а затем установить директиву getter/setter обновления между этим полем и функциями getter/setter.

Я использую $watch для обновления модели с использованием выражения setter, когда ngModelController обновляет поле в области, а другой смотрит, чтобы обновить это поле при изменении выражения getter.

Посмотрите: 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);
}

Я поместил setInterval() в свой код, чтобы изменить модель и посмотреть, правильно ли она распространяется в представлении.

Любая идея, почему существует бесконечный цикл дайджеста и как его удалить?

Ответ 1

ПРИМЕЧАНИЕ AngularJs 1.3 теперь поддерживает Getter/Setter для ng-модели. Для получения дополнительной информации см. http://docs.angularjs.org/api/ng/directive/ngModelOptions.


Я мог бы разбить бесконечный цикл с дополнительными вызовами на

ngModelCtrl.$setViewValue()

и

ngModelCtrl.$render()

в обработчиках событий. Не уверен, что это лучший способ сделать это, хотя.

Смотрите скрипту: http://jsfiddle.net/BDyAs/12/

EDIT:

Я улучшил код еще больше в

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

разделив директиву в отдельных для геттера и сеттера.

Ответ 2

Я думаю, что на вопрос о нарушении цикла дайджеста был дан ответ. Вот другой, более чистый подход к той же проблеме, который не включает $watch.

Если вам не нужно поддерживать устаревшие браузеры, используйте ECMAScript 5-аксессоры.

Просто добавьте свойство к вашему контроллеру angular:

Object.defineProperty(
    $scope, 
    "accessorWrappedMyValue",            
    {
        get : function() { return $scope.myValue; },  
        set : function(newValue) { 
                  $scope.myValue = newValue; 
                  $scope.myDerivedValue = $scope.myValue * 2;
              },
        configurable: true
    });

Теперь все, что вам нужно сделать, это ссылка accessorWrappedMyValue from ng-model так:

<input type="text" ng-model="accessorWrappedMyValue" />

Дальнейшее чтение

Этот блог имеет приятное представление об аксессуарах ES5.

Используйте эту матрицу характеристик, чтобы решить, можете ли вы пойти с ES5 или нет. Интересными являются "Getter/Setter в инициализаторе свойств" и "Object.defineProperty".