AngularJS:父范围未在指令中更新(具有独立范围)双向绑定 [英] AngularJS: Parent scope not updated in directive (with isolated scope) two way binding

查看:41
本文介绍了AngularJS:父范围未在指令中更新(具有独立范围)双向绑定的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下代码,也可以在http://jsfiddle.net/garukun/u69PT/上摆弄.

查看:

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

JS:

var testApp = angular.module('testApp', []);testApp.directive('testDirective', function ($timeout) {返回 {范围: {键:'=父项',父更新:'&'},替换:真的,模板:'<div><p>{{key}}</p>'+'<button data-ng-click="lock()">Lock</button>'+'</div>',控制器:函数($scope,$element,$attrs){$scope.lock = 函数 () {$scope.key = 'D+' + $scope.key;console.log('DIR :', $scope.key);//期望 $scope.$parent.pkey 也已经//在调用下一行之前更新.$scope.parentUpdate();//$timeout($scope.parentUpdate);//会工作.};}};});testApp.controller('testCtrl', function ($scope) {$scope.pkey = '金色';$scope.update = 函数 (k) {//需要局部变量 k 或 $scope.pkey//通过指令范围内的调用更新.console.log('CTRL:', $scope.pkey, k);$scope.pkey = 'C+' + k;console.log('CTRL:', $scope.pkey);};});

基本上,我正在设置具有独立作用域的指令,其中我从父作用域 (pkey) 双向绑定一个属性 (key),并委托一个方法 (parentUpdate) 被调用在父作用域的上下文中.

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

这可能是因为中间缺少一些 $digest 循环,因为使用 $timeout 包装 parentUpdate 调用会按预期工作.

有人能解释一下缺少什么吗?或者如何正确调用 parentUpdate?

解决方案

好的,我要尝试一下这个...看来您在 $digest 循环,双向逻辑将两者同步.详情如下:

  1. 首先你的 lock() 函数是通过点击按钮来执行的.这会更新隔离的 $scope.key 变量.注意:这不会立即更新父$scope.pKey;这通常会在下一个 $digest 循环中发生,但在这种情况下不会发生.继续阅读...
  2. lock() 中,您正在调用 parentUpdate() 来更新父对象的 $scope.pKey 变量.
  3. 然后执行 $digest 循环.当它循环到父作用域时,会正确检测到对 $scope.pKey 的更改.
  4. $scope.pKey 的更改会触发一个 watch(),它是由隔离作用域中的双向绑定创建的.这些行
  5. 由隔离作用域创建的 watch() 检查双向绑定的值是否与父级的值同步.如果不是(也不是在这种情况下),则父级的值将复制到隔离范围即使隔离范围的值也已更改并且实际上已首先更改.

Misko 关于 Angular 数据绑定的著名博文 描述了 $digest 循环方法.你在这里看到的是 $digest 改变合并的方法的一个有意识的副作用,正如源代码注释所说,parent 改变了,它有优先权...意味着您的隔离范围的更改丢失.

您在上面提到的 $timeout() 方法通过在第一个 $digest 循环中仅更改隔离范围的值来避免此问题,这允许将其复制到父作用域成功,然后调用 parentUpdate()

$compile 文档 说:

<块引用>

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

这意味着,在第 2 步,您可以通过这样的对象映射传递 pkey 的值:

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

这是更新的小提琴:http://jsfiddle.net/KbYcr/

I have the following code, which can also be fiddled on http://jsfiddle.net/garukun/u69PT/.

View:

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

Basically, I'm setting up the directive with an isolated scope, in which I'm two-way binding a property (key) from the parent scope (pkey), and also delegating a method (parentUpdate) to be called in the context of the parent scope.

Now, during a ng-click event handler in the directive, I want to invoke the parentUpdate method and do something within. When I'm invoking that method, I'm expecting my parent scope's model to have been updated. But in reality, it is not, and this is what's puzzling me.

It's probably because of some missing $digest cycles in the middle, since wrapping the parentUpdate call with $timeout would work as expected.

Could someone shed some light on what's missing? Or how to properly invoked parentUpdate?

解决方案

OK, I'm going to take a crack at this one... It seems you're changing both the isolated child AND parent variables before a $digest cycle where the bi-direction logic syncs the two. Here's the details:

  1. First your lock() function is executed by clicking on the button. This updates the isolated $scope.key variable. NOTE: This does NOT immediately update the parent $scope.pKey; that would normally happen at the next $digest cycle but does not in this case. Read on...
  2. Within lock() you are calling parentUpdate() which updates the parent's $scope.pKey variable.
  3. THEN the $digest cycle executes. When it loops it's way to the parent scope a change to $scope.pKey is correctly detected.
  4. The change to $scope.pKey triggers a watch() that was created by the bi-directional binding in the isolated scope. These lines are the critical ones..
  5. The watch() created by the isolated scope checks whether it's value for the bi-directional binding is in sync with the parent's value. If it isn't (and it's not in this scenario) the parent's value is copied to the isolated scope even though the isolated scope's value has changed also and in fact was changed first.

Misko's famous post on Angular data-binding describes the benefits of the $digest cycle approach. What you're seeing here is a conscious side-effect of the $digest's approach to change coalesence wherein, as the source code comment says, parent changed and it has precedence... and that means your isolated scope's change loses.

The $timeout() approach you noted above avoids this issue by changing only the isolated scope's value in the first $digest cycle which allows it to be copied to the parent scope successfully and THEN calling parentUpdate()

The $compile documentation says:

Often it's desirable to pass data from the isolated scope via an expression and to the parent scope, this can be done by passing a map of local variable names and values into the expression wrapper fn. For example, if the expression is increment(amount) then we can specify the amount value by calling the localFn as localFn({amount: 22}).

This means, at step #2 you could pass in your value for pkey via an object map like this:

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

Here's the updated fiddle: http://jsfiddle.net/KbYcr/

这篇关于AngularJS:父范围未在指令中更新(具有独立范围)双向绑定的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆