Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS Directive Isolate Scope Not Updating Parent

I've got a directive that has a model bound 2-ways, when calling a save() method via ng-click the parent scope isn't updated unless I call $scope.$apply() which then throws the $apply already in progress error.

I'm using ngResource, and the event has a listener calling $scope.model.$save();

Is there a work-around for this? Or am I doing something completely wrong?

.directive('editable', function(){
    return {
        restrict: 'AE',
        templateUrl: '/assets/partials/editable.html',
        scope: {
            value: '=editable',
            field: '@fieldType'
        },
        controller: function($scope){

           ...

            $scope.save = function(){
                $scope.value = $scope.editor.value;
                $scope.$emit('saved');
                $scope.toggleEditor();
            };

        }
    };
})

UPDATE

It looks like it is updating the parent after all but that the emit is being fired before the digest has finished completing. I can force it to the end of the stack using $timeout but it feels a bit hacky. Is there a better way?

$scope.$on('saved', function(){
    $timeout(function(){
        $scope.contact.$update();
    }, 0);
});
like image 253
Stephen Radford Avatar asked Mar 16 '14 18:03

Stephen Radford


1 Answers

How are you calling $scope.save ? If you use one of the angular directives, like ng-click, angular will automatically run a digest cycle for you and therefore you don't need to call $scope.$apply(). Internally, angular checks if a digest is already in progress before starting another cycle, so it will handle the issue of digest already in progress for you if you use one of the built-in directives. For example, put this in your directive's template...

<button ng-click="save()">Save</button>

If you need to call save from the controller or link function, you can do a little hack to prevent the 'digest already in progress' error. Use the $timeout service to defer the call to the end of the call stack...

$scope.save();
$timeout(function() {
    $scope.$apply();
}, 0);

We are setting the timeout to 0, so there is no real delay, but this is still enough to push the $apply call to the end of the current call stack, which allows the digest that is in progress to finish first and prevents the error. This is not ideal and could imply a larger issue with your design, but sometimes you just have to make it work

like image 184
Charlie Martin Avatar answered Sep 28 '22 06:09

Charlie Martin