AngularJS:父作用域在具有隔离作用域的指令中(双向绑定)未更新

26
我有一个带有隔离作用域的指令,其值与父作用域具有双向绑定。我调用了一个方法来更改父作用域中的值,但是该更改未应用于我的指令(双向绑定未被触发)。这个问题非常相似:AngularJS: Parent scope not updated in directive (with isolated scope) two way binding,但我没有在指令中更改值,而是仅在父作用域中更改它。我阅读了解决方案,在第五点中说到:
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  the parent's value is copied to the isolated scope.

这意味着当我的父值更改为2时,将触发一个监视器。它会检查父值和指令值是否相同 - 如果不同,则复制到指令值。但是我的指令值仍然是1...我还缺少什么吗?
HTML:
<div data-ng-app="testApp">
    <div data-ng-controller="testCtrl">
        <strong>{{myValue}}</strong>
        <span data-test-directive data-parent-item="myValue" 
            data-parent-update="update()"></span>
    </div>
</div>

JS:

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

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

                 $scope.parentUpdate();
                 //$timeout($scope.parentUpdate); // would work.

                 // expecting the value to be 2, but it is 1
                 console.log('directive :', $scope.key);
            };
        }
    };
});

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

演示代码


这里讨论了同一个问题,提供了更好的解释:https://dev59.com/fIbca4cB1Zd3GeqPSSph#27533683 - Amit G
5个回答

23
在您的控制器中更改$scope.myValue后,请使用$scope.$apply(),例如:
testApp.controller('testCtrl', function ($scope) {
    $scope.myValue = '1';
    $scope.update = function () {
        // Expecting local variable k, or $scope.pkey to have been
        // updated by calls in the directive's scope.
        console.log('CTRL:', $scope.myValue);
        $scope.myValue = "2";
        $scope.$apply();
        console.log('CTRL:', $scope.myValue);
    };
});

5
我知道有方法可以使之起作用,但问题是为什么这是必要的。这就是为什么它被称为双向绑定的原因吗? - Mdb
4
双向数据绑定是AngularJS的一个功能,它允许您在指令之外更改内容。然而,这不属于Angular JS本身的代码结构范围内。因此,为了反映这些更改,您需要使用$scope.$apply()。 - BKM
19
我遇到了“$apply already in progress”错误。我在上面提供的jsfiddle链接中尝试了相同的操作。 - Mobin Skariya

14

使用 $scope.$apply() 的答案是完全错误的。

我所见过的在指令中更新作用域的唯一方法是像这样:

angular.module('app')
.directive('navbar', function () {
    return {
        templateUrl: '../../views/navbar.html',
        replace: 'true',
        restrict: 'E',
        scope: {
            email: '='
        },
        link: function (scope, elem, attrs) {
            scope.$on('userLoggedIn', function (event, args) {
                scope.email = args.email;
            });
            scope.$on('userLoggedOut', function (event) {
                scope.email = false;
                console.log(newValue);

            });

        }
    }
});

并且在控制器中像这样发射您的事件:

$rootScope.$broadcast('userLoggedIn', user);

我觉得这种方法很不专业,希望Angular专家能看到这篇文章并提供更好的答案。但现在来看,这个被接受的答案甚至无法正常工作,只会出现“$digest already in progress”错误。


如果我在第10个位置添加指令,会发生什么...你知道吗? 回调将被调用10次。即使只有一个emit或broadcast。 - subash
根据我的经验,在某些情况下,如果您发送的对象很大,则无法使用此方法,因为会出现错误。 - HarshaXsoad

8

像接受的答案一样使用$apply()可能会导致各种错误和潜在性能问题。设置广播等工作量很大。我发现一个简单的解决方法,只需使用标准的timeout在下一个周期中触发事件(由于timeout,这将是立即的)。将parentUpdate()调用包围起来,如下所示:

$timeout(function() {
    $scope.parentUpdate();
});

对我来说完美运行。(注:未指定时,0ms是默认的超时时间)


5
这个方法之所以有效,是因为在 Angular 的 $timeout 函数末尾会调用 $apply()。这几乎相当于 setTimout(function() { /* yourCallback() */ $rootScope.apply(); }, 0),只不过它还做了一些我不太理解的额外操作。 - rich97
使用 if (!$scope.$$phase) { $scope.$apply() } 也可以避免 $apply already in progress error,但这并不能很好地解释为什么会出现这个问题。我们需要更清晰的答案。 - Alejandro García Iglesias
1
有人能解释一下为什么这个必须在下一个周期完成而不是当前周期吗? - Timothy Dalton

4

0

不要使用$scope.$apply(),尝试使用$scope.$applyAsync();


1
解释为什么建议使用$applyAsync而不是$apply会更有用。 - ralfe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接