问题 使用AngularJs中的ng-model获得getter和setter支持


我试图通过实现一个指令来获取和/从视图/模型中获取值,从而获得对ng-model的getter / setter支持。我几乎在那里,但我最终在无限$ digest循环。

我们的想法是设置ng-model =“$ someFieldToStoreInTheScope”,然后让getter / setter指令在该字段和getter / setter函数之间进行更新。

当ngModelController更新范围中的字段时,我使用$ watch使用setter表达式更新模型,当getter表达式更改时,使用另一个watch来更新该字段。

看一下: 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()来修改模型,看看它是否在视图中正确传播。

知道为什么有一个无限的摘要循环,以及如何删除它?


8524
2018-01-22 17:17


起源

有问题吗? - Davin Tryon
是的,问题是由于无限的摘要循环它不能很好地工作。为什么那个循环以及如何删除它? - sboisse
无限的摘要循环似乎是由angularjs在同一循环中传播旧值之前的任何地方传播新值的困难时间引起的。我可以通过在updateViewValue()函数中添加对ngModelCtrl。$ setViewValue()和ngModelCtrl。$ render()的调用来打破循环(小提琴: jsfiddle.net/BDyAs/12),但更准确地了解发生了什么会很有趣,如果我打破循环的方式是最好的方法。 - sboisse
我认为无限循环是因为在changeValue上,手表正在调用实际修改模型的set方法......因此再次调用手表。也许你可以试试像“if actual!= new - > update else - >什么都不做”。 - Carlos Verdes


答案:


注意 AngularJs 1.3现在支持ng-model的Getter / Setter。参考 http://docs.angularjs.org/api/ng/directive/ngModelOptions 了解更多信息。


我可以通过额外的调用打破无限循环

ngModelCtrl.$setViewValue()

ngModelCtrl.$render()

在事件处理程序中。不知道这是否是最佳方式。

看小提琴: http://jsfiddle.net/BDyAs/12/

编辑:

我进一步改进了代码

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

通过在getter和setter的单独的指令中分离指令。


8
2018-01-22 18:06



根据angularjs每周时事通讯,getter / setter支持已合并,但无法找到任何关于它的信息atat虽然 - DrogoNevets
这是个好消息。如果你能提供一个很棒的时事通讯链接! - sboisse
我想这可能是@DrogoNevets所指的(注意它是角度1.3,不包括在1.2中): docs.angularjs.org/api/ng/directive/ngModelOptions - EverPresent
它看起来像是。我在答案中添加了一个注释。感谢分享。 - sboisse


我认为关于打破摘要循环的问题已得到解答。对于不涉及的同一问题,这是一种不同的,更清晰的方法 $watch

如果您不需要支持旧版浏览器,请使用ECMAScript 5访问器。

只需向角度控制器添加属性:

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

现在您需要做的就是参考 accessorWrappedMyValue 从 ng-model 像这样:

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

进一步阅读

这个博客 有一个很好的ES5访问器介绍。

使用 这个特征矩阵 决定你是否可以使用ES5。有趣的行是“属性初始化器中的Getter / Setter”和“Object.defineProperty”。


2
2018-05-20 09:34