为什么我必须调用

为什么我必须调用

本文介绍了AngularJS - 在更改模型值的指令中,为什么我必须调用 $render?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

代码在下面,这里有一个小提琴.

angular.module('myDirective', []).directive('myDirective', function () {返回 {限制:'A',终端:真的,要求:?ngModel",链接:函数(范围、元素、属性、ngModel){scope.$watch(attrs.ngModel, function (newValue, oldValue) {//ngModel.$setViewValue(newValue + "!");if (newValue == "foo"){ngModel.$setViewValue(oldValue);/*我需要这个渲染调用来更新输入框;这可以吗?我最好的猜测是设置 new = old 可以防止脏检查,这会触发 $render()*/ngModel.$render();}});}};});函数 x($scope) {$scope.test = '这里的值';}
解决方案

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

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

没错.$setViewValue 将模型值设置为被视图更新,但您需要调用 $render 以根据(新)模型值有效地渲染视图.查看此讨论了解更多信息.

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

link: function (scope, element, attrs, ngModel) {如果(!ngModel)返回;ngModel.$parsers.unshift(function(viewValue) {if(viewValue === 'foo') {var currentValue = ngModel.$modelValue;ngModel.$setViewValue(currentValue);ngModel.$render();返回当前值;}别的返回视图值;});}

我将您的 jsFiddle 脚本更改为使用上面的代码.

angular.module('myDirective', []).directive('myDirective', function () {返回 {限制:'A',终端:真的,要求:?ngModel",链接:函数(范围、元素、属性、ngModel){如果(!ngModel)返回;ngModel.$parsers.unshift(function(viewValue) {if(viewValue === 'foo') {var currentValue = ngModel.$modelValue;ngModel.$setViewValue(currentValue);ngModel.$render();返回当前值;}别的返回视图值;});}};});函数 x($scope) {$scope.test = '这里的值';}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script><h1>Foo Fighter</h1>我讨厌foo",试着在框中输入它.<div ng-app="myDirective" ng-controller="x"><input type="text" ng-model="test" my-directive><br/>模型:{{测试}}

I made a directive designed to be attached to an element using the ngModel directive. If the model's value matches something the value should then set to the previous value. In my example I'm looking for "foo", and setting it back to the previous if that's what's typed in.

My unit tests passed fine on this because they're only looking at the model value. However in practice the DOM isn't updated when the "put back" triggers. Our best guess here is that setting old == new prevents a dirty check from happening. I stepped through the $setViewValue method and it appears to be doing what it ought to. However it won't update the DOM (and what you see in the browser) until I explicitly call ngModel.$render() after setting the new value. It works fine, but I just want to see if there's a more appropriate way of doing this.

Code is below, here's a fiddle with the same.

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';
}
解决方案

A watcher listener is only called when the value of the expression it's listening to changes. But since you changed the model back to its previous value, it won't get called again because it's like the value hasn't changed at all. But, be careful: changing the value of a property inside a watcher monitoring that same property can lead to an infinite loop.

That's correct. $setViewValue sets the model value as if it was updated by the view, but you need to call $render to effectively render the view based on the (new) model value. Check out this discussion for more information.

Finally, I think you should approach your problem a different way. You could use the $parsers property of NgModelController to validate the user input, instead of using a watcher:

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

I changed your jsFiddle script to use the code above.

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>

这篇关于AngularJS - 在更改模型值的指令中,为什么我必须调用 $render?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-31 00:04