避免观察变量循环的方法

时间:2017-11-03 21:31:46

标签: angularjs

enter image description here我有一个带有范围变量var1的控制器和一个带有范围变量var2的指令。 var2应该反映var1的值,反之亦然。我在指令中观察变量,以了解它在控制器中的变化。

问题是当指令中的监视变量发生变化时会产生一个循环(参见plunk控制台)。

为了防止这个循环,我可以在指令中创建两个范围变量var2Inputvar2Output来读取/写入控制器中的var1。只会var2Input被观看,而我在var2Output中更改的指令中更改了(在控制器中读取)。但我不想创建两个变量,因为它们总是具有相同的值。任何想法如何处理这个?

使用Javascript:

app.directive('someDirective', function () {

    var directive = {};

    directive.restrict = 'EA';

    directive.scope = {    
            var2: '='
    };

    directive.template = 'This is var2: {{var2}} <br/> ' + 
    '<button ng-click="add1()">Add 1</button>  <br/> ' + 
    '{{log}}';

    directive.link = {};

    directive.link.pre = function (scope, element, attrs) {};

    directive.link.post = function (scope, element, attrs) {

        scope.log = '';

        scope.$watch('var2', function (newValue, oldValue) {
               if (typeof newValue === 'undefined')
                  return;
          scope.var2 = newValue * 10;
          scope.log = scope.log + '<watched ' + newValue + '>';
        });

        scope.add1 = function() {
          scope.var2++;
        };
    };

    return directive;

});

2 个答案:

答案 0 :(得分:2)

行。以下是应该执行您正在寻找的代码。看看代码然后我的解释如下:

<!DOCTYPE html>
<html ng-app="myApp">
  <head>
    <title>AngularJS Example</title>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
    <script>
    var myApp = angular.module("myApp", []);

    myApp.controller('appController', function($scope) {
      $scope.var1 = 1;

      $scope.add = function(amount) {
        $scope.var1 += amount;
      }
    });

    myApp.controller('watcherController', watcherController);
    watcherController.$inject = ['$scope'];
    function watcherController($scope) {
      $scope.log = '';

      $scope.$watch('var2', function (newValue, oldValue) {
        if (typeof newValue === 'undefined') {
          return;
        }

        $scope.log = $scope.log + '\nwatched ' + newValue;
      });

      $scope.add1 = function() {
        $scope.var2++;
      };
    }

    myApp.directive('watcher', watcherDirective );
    function watcherDirective() {
      return {
        'restrict': 'EA',
        'template': 'This is var2: {{var2}} <br/><hr/><button ng-click="add1()">Add 1</button><br/><hr/><h4>LOG:</h4><pre>{{log}}</pre>',
        'controller': 'watcherController',
        'scope': {
          var2: '@'
        },
        'link': function ($scope, $element, $attrs, ctrl, transclude) {
          $attrs.$observe('var2', function(newValue, one, two) {
            $scope.var2 = parseInt(newValue,10)*10;
            $scope.log += "\nChanged on the outside"+$scope.var2+' - '+(typeof $scope.var2);
          });
        }
      };
    }
    </script>
  </head>
  <body ng-controller="appController">
    <button ng-click="add(5)">Add from outer controller</button><br/>Var1:{{var1}}<br/><hr/>
    <watcher var2="{{var1}}"></watcher>
  </body>
</html>

每次更改$scope.$watch时都会调用$scope.var2,无论谁改变它,所以我们必须小心我们在该观察者内部所做的事情。

我现在显示外部控制器的$scope.var1值,并添加了第二个按钮,用于更改外部控制器的$scope.var1。每次更改时,该值都会传递给指令。

而不是使用双向绑定'var2': '='我使用单向绑定'var2': '@'这会将$scope.var1的值转换为字符串并将其传递给指令。

然后,在指令中我添加了$attrs.$observe,让我知道外部值何时发生变化。我必须使用parseInt()将该值转换回数字。仅当外部世界更改指令的属性$attrs.$observer的值时,才会调用var2函数。

如果您需要双向绑定,则需要执行不同的操作。因此,如果指令需要能够修改外部控制器的$scope.var1的值,那么我将需要演示其他内容。

如果这没有意义,请告诉我,我会修改我的答案。

修改后的答案

这是一些修改后的代码。以下是解释:

<!DOCTYPE html>
<html ng-app="myApp">
  <head>
    <title>AngularJS Example</title>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
    <script>
    var myApp = angular.module("myApp", []);

    myApp.controller('appController', function($scope) {
      $scope.var1 = 1;

      $scope.add = function() {
        $scope.var1++;
      }

      $scope.reset = function() {
        $scope.var1 = 1;
      }
    });


    //******************* BEGIN TWO WAY BINDING
    myApp.controller('watcherController', watcherController);
    watcherController.$inject = ['$scope'];
    function watcherController($scope) {
      $scope.log = '';
      var doWatchCode = true;

      $scope.$watch('var2', function (newValue, oldValue) {
        if (typeof newValue === 'undefined') {
          return;
        }

        if (doWatchCode) {
          $scope.var2 = newValue * 10;
          $scope.log = $scope.log + '\nvar2 watched newValue(' + newValue+') - $scope.var2('+$scope.var2+')';
          doWatchCode = false;
        }
        else {
          doWatchCode = true;
        }
      });

      $scope.add1 = function() {
        doWatchCode = false;
        $scope.var2++;
      };
    }

    myApp.directive('watcher', watcherDirective );
    function watcherDirective() {
      return {
        'restrict': 'EA',
        'template': 'This is var2: {{var2}} <br/><hr/><button ng-click="add1()">Add 1</button><br/><hr/><h4>LOG:</h4><pre>{{log}}</pre>',
        'controller': 'watcherController',
        'scope': {
          var2: '='
        },
        'link': function ($scope, $element, $attrs, ctrl, transclude) {
          $attrs.$observe('var2', function(newValue) {
            $scope.log += "\nChanged on the outside"+newValue+' - '+(typeof newValue);
          });
        }
      };
    }
    //******************* END TWO WAY BINDING
    </script>
  </head>
  <body ng-controller="appController">
    <button ng-click="reset()">Reset Var1</button><br/>
    <button ng-click="add()">Add from outer controller</button><br/>Var1:{{var1}}<br/><hr/>
    <watcher var2="var1"></watcher>
  </body>
</html>

我在这里做的是添加一个门变量doWatchCode,允许你的$watch函数执行或不执行操作。

doWatchCodetrue时,您的$watch函数可以执行任何需要执行的操作。如果它更改var2的值,则必须将doWatchCode的值更改为false。这样做的原因是更改var2会导致再次调用$watch函数。但是,这次doWatchCode将为false并阻止var2的值再次更新。在退出函数之前,doWatchCode的值必须设置回true

add功能中,您必须将doWatchCode设置为false,以防止您的内部功能再次发生繁殖。

var2的值从外部doWatchCode更改时,true应该是$watch,这将允许您的ArrayList<String> names = new ArrayList<>(); BufferedReader input = new BufferedReader(new InputStreamReader(System.in)); boolean access; while (true) { access = true; System.out.println("Type your first name."); String firstName = input.readLine(); //System.out.println(); System.out.println("Now type your last name."); String lastName = input.readLine(); String fullName = firstName + " " + lastName; if(names.size()==0) { names.add(fullName); continue; } for (String name : names) { if (name.equals(fullName)) { System.out.println("That is not your name. Please type in your real name."); access = false; break; } } if(access) { names.add(fullName); System.out.println(); System.out.println(firstName + " " + lastName); } } 功能完成其工作。

如果有效,请告诉我。

答案 1 :(得分:1)

第一次只需要观察者即可获得第一次text更改。之后你可以取消它。另外CodeMirror是3d派对插件,您可以添加 $timeout来触发摘要周期。这是一个有效的固定演示:

var app = angular.module('app', []);

app.controller('myCtl', function($scope) {

     $scope.text = "this is the text";

     $scope.showText = function() {
       alert($scope.text);
     };

});

app.directive('editor', function ($timeout) {

    var directive = {};

    directive.restrict = 'EA';

    directive.scope = {    
            text: '='
    };

    directive.template = '<textarea id="cm"> </textarea>';

    directive.link = function (scope, element, attrs) {

      scope.editor = CodeMirror.fromTextArea(document.getElementById('cm'), {
          lineNumbers: true
      });
      scope.editor.setSize(300, 100);


      var canceler = scope.$watch('text', function (newValue, oldValue) {
            if (typeof newValue === 'undefined')
                  return;

               scope.editor.setValue(newValue);
               canceler();
        });


      scope.editor.on("change", function(cm, change) {
        $timeout(function(){
           scope.text = scope.editor.getValue();
        });
      });
    };

    return directive;
});

Demo Plunker