0

I'm trying to write an animation directive, that changes the width of an element and makes a change in the model afterwords. Here is my code:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
    </head>
    <body>
        <div ng-app="myApp" ng-controller="MyCtrl">
            <button ng-init="speed=20000" ng-click="model.width = model.width + 100;"> + </button>
            <button ng-click="model.width = model.width - 100;"> - </button>
            <div animate-to-width="model.width" speed="{{speed}}" done="model.done()" style="background-color: #f00; width: 100px;">w:{{model.width}}|a:{{model.a}}|b:{{model.b}}</div>
        </div>

        <script src="components/jquery/jquery.js"></script>
        <script src="components/angular-unstable/angular.js"></script>
        <script>

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

myApp.directive('animateToWidth', function() {
    return {
        'restrict': 'A',
        'link' : {
            'post': function(scope, element, attrs) {
                scope.$watch(
                    attrs.animateToWidth,
                    function (newValue) {
                        element.animate(
                            {'width': newValue + 'px'},
                            attrs.speed,
                            function () {
                                scope.model.a++;
                                //scope[attrs.done]();
                            }
                        );
                    }
                );
            }
        }
    };
});

function MyCtrl($scope) {
    $scope.model = {};
    $scope.model.width = 100;
    $scope.model.a = 0;
    $scope.model.b = 0;
    $scope.model.done = function () { $scope.model.b++; };
}

        </script>
    </body>
</html>

When I run this code the second parameter of the jQuery .animate() function seems not to affect the animation speeds and the callback (third parameter) will be called immediately instead after the animation has been finished.

My second problem is, that I would want to pass a callback from the controller into the directive and I don't know how to achieve this.

EDIT

Here is the solution (thanks to @banana-in-black):

http://plnkr.co/edit/D9TJHBYjtnxTve0xZpBS?p=preview

And here without those width values in the controller:

http://plnkr.co/edit/eiy99Crh57Jc78RhAsRt?p=preview

1 Answer 1

1

What you get from attrs.speed is String, and there's no effect if you set duration as String to jQuery.animate(). So make it a Number could solve the speed issue.

The callback after jQuery.animate() is called outside "the angular world", so you have to use $apply to make sure angular knows what happened to models.

If you didn't assign scope to directive, it will use the existing scope on the element. In this case, the div[animate-to-width] uses the same scope as MyCtrl does. You can just call your function which is set to scope in your controller.

scope.$watch(
    attrs.animateToWidth,
    function (newValue) {
        element.animate(
            {'width': newValue + 'px'},
            attrs.speed * 1,
            function () {
                scope.$apply(function() {
                    scope.model.a++;
                    scope.model.done();
                });
            }
        );
    }
);

Example in Plunker

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you! Making speed an integer solves the two problems, that speed has been ignored and the callback has been called immediately (not after the animation has been finished). I know about $apply, thank you. My problem here was, that the callback has been executed immediately and a has been incremented, although it has not been set into $apply. I don't want to hardcode the controller function call in the directive, because I don't want to let the directive know anything about the controller. But I solved the callback problem: plnkr.co/edit/D9TJHBYjtnxTve0xZpBS?p=preview

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.