AngularJS - In a directive that changes the model value, why do I have to call $render?

后端 未结 1 1464
南方客
南方客 2020-12-14 17:29

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 valu

相关标签:
1条回答
  • 2020-12-14 17:56

    Our best guess here is that setting old == new prevents a dirty check from happening

    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.

    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.

    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>

    0 讨论(0)
提交回复
热议问题