22

我制作了一个指令,旨在使用 ngModel 指令附加到元素。如果模型的值与某个值匹配,则该值应设置为先前的值。在我的示例中,我正在寻找“foo”,如果这是输入的内容,则将其设置回以前的值。

我的单元测试在这方面通过了很好的测试,因为他们只查看模型值。然而实际上,当“放回”触发时,DOM 不会更新。我们最好的猜测是设置 old == new 可以防止脏检查的发生。我逐步完成了 $setViewValue 方法,它似乎正在做它应该做的事情。但是,它不会更新 DOM(以及您在浏览器中看到的内容),直到我在设置新值后明确调用 ngModel.$render()。它工作正常,但我只是想看看是否有更合适的方法来做到这一点。

代码在下面,这是一个相同的小提琴

angular.module('myDirective', [])
    .directive('myDirective', function () {
    return {
        restrict: 'A',
        terminal: true,
        require: "?ngModel",
        link: function (scope, element, attrs, ngModel) {
            scope.$watch(attrs.ngModel, function (newValue, oldValue) {
                //ngModel.$setViewValue(newValue + "!");   

                if (newValue == "foo")
                {
                    ngModel.$setViewValue(oldValue);   
                    /* 
                        I Need this render call in order to update the input box; is that OK?
                        My best guess is that setting new = old prevents a dirty check which would trigger $render()
                    */
                    ngModel.$render();
                }
            });
        }
    };
});

function x($scope) {
    $scope.test = 'value here';
}
4

1 回答 1

33

我们最好的猜测是设置 old == new 可以防止脏检查发生

只有当它正在侦听的表达式的值发生变化时,才会调用观察者侦听器。但是由于您将模型更改回其先前的值,它不会再次被调用,因为它就像值根本没有改变一样。但是,请注意:在监视同一属性的观察者中更改属性的值可能会导致无限循环。

但是,它不会更新 DOM(以及您在浏览器中看到的内容),直到我在设置新值后明确调用 ngModel.$render()。

这是正确的。$setViewValue将模型值设置为好像它已由视图更新,但您需要调用$render以根据(新)模型值有效呈现视图。查看此讨论以获取更多信息。

最后,我认为您应该以不同的方式处理您的问题。您可以使用 的$parsers属性NgModelController来验证用户输入,而不是使用观察者:

link: function (scope, element, attrs, ngModel) {
  if (!ngModel) return;

  ngModel.$parsers.unshift(function(viewValue) {
    if(viewValue === 'foo') {                 
      var currentValue = ngModel.$modelValue;
      ngModel.$setViewValue(currentValue);
      ngModel.$render(); 
      return currentValue;
    }
    else 
      return viewValue;
  });
}

我更改了您的jsFiddle 脚本以使用上面的代码。

angular.module('myDirective', [])
.directive('myDirective', function () {
  return {
    restrict: 'A',
    terminal: true,
    require: "?ngModel",
    link: function (scope, element, attrs, ngModel) {
      if (!ngModel) return;

      ngModel.$parsers.unshift(function(viewValue) {
        if(viewValue === 'foo') {                 
          var currentValue = ngModel.$modelValue;
          ngModel.$setViewValue(currentValue);
          ngModel.$render(); 
          return currentValue;
        }
        else 
          return viewValue;
      });
    }
  };
});

function x($scope) {
  $scope.test = 'value here';
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<h1>Foo Fighter</h1>
I hate "foo", just try and type it in the box.
<div ng-app="myDirective" ng-controller="x">
  <input type="text" ng-model="test" my-directive>
  <br />
  model: {{test}}
</div>

于 2013-10-03T20:06:27.387 回答