How to call angularjs animation from controller method?

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

I came up with something that doesn't suit me (see code below).

The problem is that for my animation I need to keep track of the state of the $ 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 anyone please consult for a cleaner solution?

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

      

+3


source to share


3 answers


Here is the solution I used (see code below). This is an adaptation of our proposed Liamns 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>

      

0


source


I would suggest you wrap the form in a directive and fire the animation through an event. for example in a controller you would do:

$scope.$broadcast('FORM_ERROR');

      

and in the directive do something more like



scope.$on('FORM_ERROR', function() { // code to trigger animation goes here });

      

It makes sense to treat this as an event to me, because then it has a strict lifecycle; the event is dispatched, the event is processed. The scope variable will hang, although it no longer makes any difference. You might even need to "reset" code back to its original state so that the animation can run again, which adds unnecessary complexity.

+4


source


You will have a different problem: your animation will not be reset after the first failure, so a consecutive failure will not re-animate. I am afraid there is no way to do this using pure CSS.

First you need to use the validation services provided by Angular and then decouple the error animation logic.

Here is a working solution, with the util factory prefix working fine.

  • First check form.$invalid

    inside your function signin(formCtrl)

    if the $ form is invalid or the response from the service returns a login error, then call the function that will handle the animation.

  $scope.signin = function(form){
    if(form.$invalid){
      $scope.animateError();
    }
  };

      


  • Modify your CSS to add just the function and duration of the animation. So when the function is called showAnimationError

    , it will take the form element and add style animationName

    / webkitAnimationName

    with value shake

    .

Also don't forget to add an event listener for the end of the animation so you can clear that style for successive calls to showAnimationError

/* css */
.shake {
    -webkit-animation-duration: 400ms;
    -webkit-animation-timing-function: ease-in-out;
    animation-duration: 400ms;
    animation-timing-function: ease-in-out;
}

      


$scope.animateError = function(){
  if(!resetAnimationHandler){
    addResetAnimationHandler();
  }
  myForm.style[prefixUtil.animationName] = 'shake';
};

      

Hope this helps you.

+1


source







All Articles