【问题标题】:changing a controller scope variable in a directive is not reflected in controller function更改指令中的控制器范围变量不会反映在控制器功能中
【发布时间】:2014-12-17 19:07:10
【问题描述】:

在我的指令中,我有一个控制器变量,当您按下指令中的按钮时页面会递增。但是,调用控制器函数的下一行scope.alertPage() 并未反映此更改。请注意,当您单击按钮页面时仍会提示为 1!

我知道我可以通过在控制器中添加 $scope.$apply 来解决此问题,但随后我收到错误消息,指出摘要已经发生。

Plunker

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

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

  $scope.page = 1;

  $scope.alertPage = function() {
    alert($scope.page);
  }

})

app.directive('incrementer', function() {
  return {
    scope: {
      page: '=',
      alertPage: '&'
    },
    template: '<button ng-click="incrementPage()">increment page</button>',

    link: function(scope, elem, attrs) {
      scope.incrementPage = function() {
          scope.page += 1;
          scope.alertPage();
      }
    }
  }
})

html模板:

  <body ng-app='app' ng-controller='myCtrl'>
    page is {{page}}

    <incrementer page='page' alert-page='alertPage()'></incrementer>
  </body>

【问题讨论】:

  • 将其置于 $timeout 或以 2 方式绑定到具有该属性的对象。由于它是 2 路绑定,因此原始的 2 路绑定值将仅在下一个摘要循环期间更新。即尝试$timeout(scope.alertPage) 或其他方式,您可以在您的控制器集$scope.page = {value : 1} 中尝试并在指令中执行$scope.page += 1; 并休息您正在做的任何事情

标签: angularjs angularjs-directive


【解决方案1】:

它不立即显示更新值的原因是因为 2 路绑定仅在摘要循环期间更新父(或指令的消费者范围)范围的绑定值。触发 ng-click 后会发生摘要循环。因此控制器中的$scope.page 尚未更新。您可以通过使用timeout 以多种方式解决此问题,该timeout 将推迟在摘要周期结束时运行的操作。您也可以通过设置一个将值保存为 2 路绑定属性的对象来实现。由于 2-way 绑定属性和父作用域共享相同的对象引用,您将立即看到更改。

方法 1 - 使用超时:

  scope.incrementPage = function() {
     scope.page += 1;
     $timeout(scope.alertPage)
  }  

方法 2 - 绑定对象:

 //In your controller
 $scope.page2 = {value:1};

//In your directive 
scope.incrementPage = function() {
     scope.page.value += 1;
     scope.alertPage();
 }  

方法3 - 使用带参数的函数绑定传递值:

//In your controller
$scope.alertPage = function(val) {
  alert(val);
}

<!--In the view-->
<div incrementer page="page" alert-page="alertPage(page)"></div>

//In the directive
scope.incrementPage = function() {
     scope.page += 1;
     scope.alertPage({page:scope.page});
 }  

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

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

  $scope.page = 1;
  $scope.page2 = {value:1};
  
  $scope.alertPage = function() {
    alert($scope.page);
  }
  
  $scope.alertPage2 = function() {
    alert($scope.page2.value);
  }

})

app.directive('incrementer', function($timeout) {
  return {
   
    scope: {
      page: '=',
      alertPage: '&',
      page2:"=",
      alertPage2: '&'
    },
    template: '<button ng-click="incrementPage()">increment page</button>',

    link: function(scope, elem, attrs) {
      scope.incrementPage = function() {
          scope.page += 1;
          scope.page2.value += 1;
          $timeout(function(){ scope.alertPage() });
          scope.alertPage2();
      }
    }
  }
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.5/angular.min.js"></script>
<div ng-app="app" ng-controller="myCtrl">
  <div incrementer page="page" alert-page="alertPage()" page2="page2" alert-page2="alertPage2()"></div>
  </div>

【讨论】:

    【解决方案2】:

    你可以通过引用传递变量,那么更新将是即时的(因为你不会复制它,而只是传递它在内存中的位置)。

    查看:

    <incrementer page="data" alert-page='alertPage()'></incrementer>
    

    指令:

    link: function(scope, elem, attrs) {
      scope.incrementPage = function() {
          scope.page.page += 1;
          scope.alertPage();
      }
    

    【讨论】:

    • 是的,类似于我的示例中的方法 2.. 想法相同.. 干杯.. :)
    【解决方案3】:

    问题是时间线关闭了。

    1. 按钮点击,incrementPage()
    2. 指令范围值递增(现在为 2)
    3. alertPage() 父范围值读取(仍为 1)
    4. 作为摘要的一部分更新了父范围(现在为 2)

    要解决这个问题,您需要在摘要循环之后调用警报函数(例如$timeout),或者您需要观察父范围的变化。

    // in controller
    $scope.$watch('page', function (currentValue, previousValue) {
      // initially triggered with same value
      if (currentValue > previousValue) {
        alert(currentValue)
      }
    })
    

    然后自然地改变值。

    // in directive html
    <button ng-click="page = page + 1">
    

    【讨论】:

    • 真的要为此制作手表吗?函数绑定的目的在这里完全失去了恕我直言。我敢肯定粘贴的例子只是一个例子,原始代码一定比这更复杂。
    • @PSL 我认为 $watch 是一个优雅的解决方案。使用 $timeout 会触发一个全新的摘要循环 afaik。
    • 在这种情况下并不是真的会有一个 ngclick 超时的摘要周期将它自己添加到 asyc 队列中,该队列将与相同的摘要周期一起运行.. 你也不应该只是创建手表.. 在这种情况下确实没有必要,并且会增加摘要循环。
    【解决方案4】:

    尝试以稍微不同的方式执行此操作。传入一个函数来执行增量而不是在指令内递增

    HTML

            <incrementer page='incPage()' alert-page='alertPage()'></incrementer>
    

    控制器

              $scope.incPage = function() {    // Function to increment
                 $scope.page++;
             };
    

    在指令中

             scope: {
               page: '&',      // Receive like this
               alertPage: '&'
             },
    
    
              link: function(scope, elem, attrs) {
                   scope.incrementPage = function() {
                     scope.page();          // Call as function
                     scope.alertPage();
                   }  
              }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-01-16
      • 2015-08-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多