一尘不染

AngularJS-在更改模型值的指令中,为什么我必须调用$ render?

angularjs

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

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

代码如下,这是一个小玩意儿

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';
}

阅读 232

收藏
2020-07-04

共1个答案

一尘不染

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

仅当正在监听的表达式的值发生更改时,才会调用观察者监听器。但是由于您已将模型改回其先前的值,因此不会再次调用它,因为好像值根本没有改变。但是要小心:在监视同一属性的观察者内部更改属性的值可能导致无限循环。

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

没错
$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>
2020-07-04