Search code examples
angularjsangularjs-animation

How to trigger an angularjs animation from a controller method?


I would like to trigger an angular animation from a controller method.

I have come up with something that I am not satisfied with (see code below).

The issue is that for my animation to work, I need to track the state of a $scope variable i.e. $scope.shake:

$scope.signin = function (formCtrl) {
    $scope.shake = false;
    if ($scope.credentials) {
        signinService.signin($scope.credentials, function (status, memberRole) {
                $scope.shake = false;
                //TODO: necessary to check status?
                if (status === 200) {
                    var memberType;
                    if (memberRole === 'ROLE_BASIC_PARENTS') {
                        memberType = 'parents';
                    }
                    if (memberRole === 'ROLE_BASIC_CHILDCARE_WORKER') {
                        memberType = 'childcare-worker';
                    }
                    $rootScope.globals = {
                        memberType: memberType,
                        authenticated: 'OK'
                    }

                    $cookies.globalsMemberType = $rootScope.globals.memberType;
                    $cookies.globalsAuthenticated = $rootScope.globals.authenticated;

                    $state.go('dashboard', {memberType: memberType});
                }
            },
            function () {
                $scope.shake = true;
            });
    }
    else {
        $scope.shake = true;
    }
};

<form ng-class="{shake: shake}" name="formCtrl" ng-submit="signin(formCtrl)" novalidate>

Can someone please advise a cleaner solution?

edit 1:

Here is the css code as requested:

@-webkit-keyframes shake {
    0% {
        -webkit-transform: translateX(0);
        transform: translateX(0);
    }

    12.5% {
        -webkit-transform: translateX(-6px) rotateY(-5deg);
        transform: translateX(-6px) rotateY(-5deg);
    }

    37.5% {
        -webkit-transform: translateX(5px) rotateY(4deg);
        transform: translateX(5px) rotateY(4deg);
    }

    62.5% {
        -webkit-transform: translateX(-3px) rotateY(-2deg);
        transform: translateX(-3px) rotateY(-2deg);
    }

    87.5% {
        -webkit-transform: translateX(2px) rotateY(1deg);
        transform: translateX(2px) rotateY(1deg);
    }

    100% {
        -webkit-transform: translateX(0);
        transform: translateX(0);
    }
}

@keyframes shake {
    0% {
        -webkit-transform: translateX(0);
        transform: translateX(0);
    }

    12.5% {
        -webkit-transform: translateX(-6px) rotateY(-5deg);
        transform: translateX(-6px) rotateY(-5deg);
    }

    37.5% {
        -webkit-transform: translateX(5px) rotateY(4deg);
        transform: translateX(5px) rotateY(4deg);
    }

    62.5% {
        -webkit-transform: translateX(-3px) rotateY(-2deg);
        transform: translateX(-3px) rotateY(-2deg);
    }

    87.5% {
        -webkit-transform: translateX(2px) rotateY(1deg);
        transform: translateX(2px) rotateY(1deg);
    }

    100% {
        -webkit-transform: translateX(0);
        transform: translateX(0);
    }
}

.shake {
    -webkit-animation: shake 400ms ease-in-out;
    animation: shake 400ms ease-in-out;
}

Solution

  • Here is the solution I ended up using (see code below). It is an adaptation of Liamnes's proposed solution.

    angular.module('signin')
        .controller('SigninCtrl', ['$scope', '$rootScope', '$cookies', '$state', '$animate', 'signinService', function ($scope, $rootScope, $cookies, $state, $animate, signinService) {
    
            var setPersonalInfo = function (param) {
                return signinService.setPersonalInfo(param.headers, $rootScope);
            };
            var goToDashboard = function (memberType) {
                $state.go('dashboard', {memberType: memberType});
            };
            var reportProblem = function () {
                $scope.formCtrl.username.$setValidity('username.wrong', false);
                $scope.formCtrl.password.$setValidity('password.wrong', false);
                $scope.$broadcast('SIGNIN_ERROR');
            };
            var resetForm = function(formCtrl){
                formCtrl.username.$setValidity('username.wrong', true);
                formCtrl.password.$setValidity('password.wrong', true);
            };
    
            $scope.signin = function (formCtrl) {
    
                resetForm(formCtrl);
    
                if (formCtrl.$valid) {
                    signinService.signin($scope.credentials).then(setPersonalInfo).then(goToDashboard).catch(reportProblem);
                }
                else {
                    $scope.$broadcast('SIGNIN_ERROR');
                }
            }
        }])
        .directive('shakeThat', ['$animate', function ($animate) {
            return {
                require: '^form',
                scope: {
                    signin: '&'
                },
                link: function (scope, element, attrs, form) {
                    scope.$on('SIGNIN_ERROR', function () {
                        $animate.addClass(element, 'shake').then(function () {
                            $animate.removeClass(element, 'shake');
                        });
                    });
                }
            };
        }]);
    

    HTML:

    <form shake-that name="formCtrl" ng-submit="signin(formCtrl)" novalidate>