AngularJS: Parent scope is not updated in directiv

2020-02-03 05:04发布

I have a directive with isolated scope with a value with two way binding to the parent scope. I am calling a method that changes the value in the parent scope, but the change is not applied in my directive.(two way binding is not triggered). This question is very similar:

AngularJS: Parent scope not updated in directive (with isolated scope) two way binding

but I am not changing the value from the directive, but changing it only in the parent scope. I read the solution and in point five it is said:

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.

Which means that when my parent value is changed to 2, a watch is triggered. It checks whether parent value and directive value are the same - and if not it copies to directive value. Ok but my directive value is still 1 ... What am I missing ?

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

Fiddle

标签: angularjs
5条回答
姐就是有狂的资本
2楼-- · 2020-02-03 05:44

Use $scope.$apply() after changing the $scope.myValue in your controller like:

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);
    };
});
查看更多
家丑人穷心不美
3楼-- · 2020-02-03 05:51

Instead of using $scope.$apply(), try using $scope.$applyAsync();

查看更多
干净又极端
4楼-- · 2020-02-03 06:02

The answer Use $scope.$apply() is completely incorrect.

The only way that I have seen to update the scope in your directive is like this:

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

            });

        }
    }
});

and emitting your events in the controller like this:

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

This feels like such a hack I hope the angular gurus can see this post and provide a better answer, but as it is the accepted answer does not even work and just gives the error $digest already in progress

查看更多
5楼-- · 2020-02-03 06:02

Using $apply() like the accepted answer can cause all sorts of bugs and potential performance hits as well. Settings up broadcasts and whatnot is a lot of work for this. I found the simple workaround just to use the standard timeout to trigger the event in the next cycle (which will be immediately because of the timeout). Surround the parentUpdate() call like so:

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

Works perfectly for me. (note: 0ms is the default timeout time when not specified)

查看更多
放荡不羁爱自由
6楼-- · 2020-02-03 06:02

One thing most people forget is that you can't just declare an isolated scope with the object notation and expect parent scope properties to be bound. These bindings only work if attributes have been declared through which the binding 'magic' works. See for more information:

https://umur.io/angularjs-directives-using-isolated-scope-with-attributes/

查看更多
登录 后发表回答