AngularJs animating in directive doesn't work

2019-08-12 07:43发布

I can't figure out why the following animation doesn't work as it should:

app.directive('openMenu', ['$animate', '$timeout', function($animate, $timeout) {
    return {
        link: function(scope, elem) {
            elem.bind('click', function() {

                if(elem.is(':animated'))
                    return;

                $timeout(function() {
                    $animate.addClass(elem, 'see');
                }, 0);

            });
        }
    }
}]);

And in this one the animation doesn't work at all (and class is not added either):

app.directive('openMenu', ['$animate', function($animate) {
    return {
        link: function(scope, elem) {
            elem.bind('click', function() {

                if(elem.is(':animated'))
                    return;

                $animate.addClass(elem, 'see');

            });
        }
    }
}]);

In the second code snippet I only removed $timeout, which is 0, I tried to use self-firing functions to check - animating works only when I am using timeouts. Can someone explain me why?

middle {
    margin-left: 0;
}
middle.see {
    margin-left: 270px;
}
.middle.see-add {
    -webkit-transition: margin-left 300ms;
    -moz-transition: margin-left 300ms;
    -ms-transition: margin-left 300ms;
    -o-transition: margin-left 300ms;
    transition: margin-left 300ms;
    margin-left: 0;
}
.middle.see-add.see-add-active {
    margin-left: 270px;
}

Here is the markup:

<div class="middle" open-menu></div>

3条回答
我欲成王,谁敢阻挡
2楼-- · 2019-08-12 07:51

Your problem is that you happen to be out of the Angular life-cycle. You are using .bind so Angular will not be notified whenever something has changed. The $timeout works because it's a one of the wrappers to notify Angular about an outside change. You could also use $apply. Both, the $timeout and $apply take a callback which will be executed. After the callback is finished Angular starts a digest beginning at the rootScope. Now every binding will be updated and everything should work properly.

scope.$apply(function() {
    $animate.addClass(elem, 'see');
});
查看更多
Animai°情兽
3楼-- · 2019-08-12 07:52

Since your directive uses jQuery and jQuery modify the DOM, we need to tell angular about it. To do so, you need to do $scope.$apply but you would run into the error : "digest already in progress".

The code executed inside the $timeout guarantee that your code will be safely executed on the next digest cycle.

0 is the default value, you don't even need to specify it. You could simply write :

 $timeout(function() {
    $animate.addClass(elem, 'see');
 });

The $timeout service is simply a convenient service equivalent to :

var timeout = setInterval(function(){
    // do stuff
    $scope.$apply();
}, 0); 

You can find extensive information about the digest mechanism in the official documentation : https://docs.angularjs.org/error/$rootScope/inprog

查看更多
祖国的老花朵
4楼-- · 2019-08-12 07:54

I think it's because elem.bind('click') is a jQuery function, which is running outside of the angular digest phase.

Probably this would work as well instead of using $timeout:

scope.$apply(function() {
     $animate.addClass(elem, 'see');
})'
查看更多
登录 后发表回答