AngularJS:父范围未在指令中更新(使用隔离范围)双向绑定

问题描述:

我有以下代码,它也可以在http://jsfiddle.net/garukun/u69PT/上拨弄。AngularJS:父范围未在指令中更新(使用隔离范围)双向绑定

检视:

<div data-ng-app="testApp"> 
    <div data-ng-controller="testCtrl"> 
     <strong>{{pkey}}</strong> 
     <span data-test-directive data-parent-item="pkey" 
      data-parent-update="update(pkey)"></span> 
    </div> 
</div> 

JS:

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

testApp.directive('testDirective', function ($timeout) { 
    return { 
     scope: { 
      key: '=parentItem', 
      parentUpdate: '&' 
     }, 
     replace: true, 
     template: '<div><p>{{key}}</p>' + 
      '<button data-ng-click="lock()">Lock</button>' + 
      '</div>', 
     controller: function ($scope, $element, $attrs) { 
      $scope.lock = function() { 
       $scope.key = 'D+' + $scope.key; 
       console.log('DIR :', $scope.key); 

       // Expecting $scope.$parent.pkey to have also been 
       // updated before invoking the next line. 
       $scope.parentUpdate(); 
       // $timeout($scope.parentUpdate); // would work. 
      }; 
     } 
    }; 
}); 

testApp.controller('testCtrl', function ($scope) { 
    $scope.pkey = 'golden'; 
    $scope.update = function (k) { 
     // Expecting local variable k, or $scope.pkey to have been 
     // updated by calls in the directive's scope. 
     console.log('CTRL:', $scope.pkey, k); 
     $scope.pkey = 'C+' + k; 
     console.log('CTRL:', $scope.pkey); 
    }; 
}); 

基本上,我建立与分离的范围的指令,其中,我双向结合的性质(键)从父范围(pkey)开始,还委派一个方法(parentUpdate)在父范围的上下文中调用。

现在,在指令中的ng-click事件处理程序期间,我想要调用parentUpdate方法并在其中执行某些操作。当我调用该方法时,我期待我的父范围的模型已经更新。但事实上并非如此,这令我感到困惑。

这可能是由于中间缺少$ digest周期,因为用$ timeout包装parentUpdate调用将按预期工作。

难道有人会对缺失的东西有所了解吗?或者如何正确调用parentUpdate?

+0

好的,所以在这里定位一下......你想知道为什么''scope.key ='D +'+ $ scope.key;''lock()'函数似乎没有任何效果, 对? – jandersen

好吧,我要对这一个进行破解......看起来你正在改变孤立子和父变量,在双向逻辑同步这两个循环的$digest循环之前。以下是详细信息:

  1. 首先您的lock()函数通过单击按钮来执行。这会更新隔离的$scope.key变量。 注意:这不是立即更新父母$scope.pKey;通常会在下一个$digest周期发生,但在这种情况下不会发生。请继续阅读...
  2. lock()以内您正在致电parentUpdate(),它更新了父母的$scope.pKey变量。
  3. 然后执行$digest循环。当它循环到父范围时,正确检测到对$scope.pKey的更改。
  4. $scope.pKey的更改会触发由隔离范围中的双向绑定创建的watch()These lines是关键的。
  5. 由隔离范围创建的watch()检查双向绑定的值是否与父项的值同步。如果不是(并且在这种情况下不是这样),即使隔离范围的值也发生了变化,并且事实上首先被更改了,但父级的值被复制到隔离范围

Misko's famous post on Angular data-binding描述了$digest循环方法的好处。您在这里看到的是$digest的改变共存的方法的有意识的副作用,其中源代码注释说parent changed and it has precedence ...并且这意味着您的隔离范围的更改会丢失。

你上面提到的$timeout()方法,通过在第一$digest周期,使之能够成功复制到父范围,然后调用只改变隔离范围的值避免了这个问题parentUpdate()

$compile documentation说:

通常希望将数据从隔离范围通过表达式传递到父范围,这可以通过将局部变量名称和值的映射传递到表达式包装函数fn来完成。例如,如果表达式是增量(金额),那么我们可以通过调用localFn作为localFn({amount:22})来指定金额值。

这意味着,在步骤#2你可以在你的价值通过一个对象映射像这样通过对pkey

parentUpdate({pkey: 'D+' + $scope.key }) 

下面是更新小提琴:http://jsfiddle.net/KbYcr/

+0

感谢您的解释,这真的很有帮助!虽然我明白这是由于消化周期的设计。我正在寻找一种方法来使独立作用域的属性构建在父作用域的属性之上,而不必调用超时函数。在这种情况下,由于我们已经处于摘要循环中,因此我们无法再调用$ digest或$ apply。理想情况下,我想调用evalAsync,但这似乎也不起作用。如果可以绕过这种设计的角度袖子里有一个技巧,那就太棒了。 – Steve

+0

我认为这只是一个窍门:-)我刚刚更新了我的答案,上面详细介绍了一个新的jsFiddle,稍作改动就证明了这一点。 – jandersen

+0

优秀的解释和例子。非常感谢! –

使用$scope.$apply()代替$scope.$digest()也起作用。这也会触发rootScope上的摘要。