Slide left/right with angular ui-router on state change + back button (for mobile usage)
Asked Answered
T

3

8

I use Angular ui-router and ng-animate for a hybrid mobile app. So I would like to use mobile standard transitions between screens (or states). Going deeper into the application, slide-left. Going back up (with the back button): slide-right. Nothing fancy and working with the normal ng-route. However, this should also work with ui-router. In fact, slide-left works fine, the problem arises when going back. It applies the class slide-left on the "original" div, but applies slide-right on the copy or ng-animate div. This is causing the animations to "cross", not nice.

The examples out there are not working in my case.

Index.html:

<div class="container">
    <div ui-view class="view"></div>
</div>

In main.css:

.slide-left.ng-enter,
.slide-left.ng-leave,
.slide-right.ng-enter,
.slide-right.ng-leave {
    position: absolute;
    top: 0px; right: 0; bottom: 0; left: 0;
    overflow-x: hidden;
    background: inherit;
    -ms-transition: 5.5s ease-in-out;
    -webkit-transition: 5.5s ease-in-out;
    transition:  5.5s ease-in-out;
}


.slide-left.ng-enter {
    z-index: 100;
    -webkit-transform: translateX(100%);
    transform: translateX(100%);
}

.slide-left.ng-enter.ng-enter-active {
    -webkit-transform: translateX(0);
    transform: translateX(0);
}

.slide-left.ng-leave {
    z-index: 101;
    -webkit-transform: translateX(0);
    transform: translateX(0);
}

.slide-left.ng-leave.ng-leave-active {
    -webkit-transform: translateX(-100%);
    transform: translateX(-100%);
}

.slide-right.ng-enter {
    z-index: 101;
    -webkit-transform: translateX(-100%);
    transform: translateX(-100%);
}

.slide-right.ng-enter.ng-enter-active {
    -webkit-transform: translateX(0);
    transform: translateX(0);
}

.slide-right.ng-leave {
    z-index: 100;
    -webkit-transform: translateX(0);
    transform: translateX(0);
}

.slide-right.ng-leave.ng-leave-active {
    -webkit-transform: translateX(100%);
    transform: translateX(100%);
}

In my routes.js: I have in the abstract view set the ng-class="slide".

 $stateProvider
        .state('enrollments', {
            abstract: true,
            url: '/tutorProcessEnrollments',
            template: '<div class="scroller" ui-view ng-class="slide" ></div>'
        })
        .state('enrollments.list', {
            url: '',
            templateUrl: 'views/tutorProcessEnrollments.list.html',
            controller: 'TutorEnrollmentsCtrl'
        })
        .state('enrollments.enrollment', {
            url: '/:enrollment_id',
            templateUrl: 'views/tutorProcessEnrollments.enrollment.html',
            controller: 'TutorEnrollmentCtrl'
        })

in App.js

.controller('MainCtrl', ['$scope', '$rootScope', '$window', '$location', '$state', function ($scope, $rootScope, $window, $location, $state) {

    $scope.slide = 'slide-left';

    // Implement own state.goTo functionality, to set the slide-left default on every menu action.
    $rootScope.goTo = function (route, param) {
        $scope.slide = 'slide-left';

        $state.go(route, param);
    };


    // Function for going back in the history. The back button is hidden on items where no sub items/states\ are available.
    $rootScope.back = function () {
        if ($scope.slide === 'slide-left') {

            $scope.slide = 'slide-right';

        }

        $window.history.back();

    };

}]);

As you can see, I use the $window.history.back() to navigate back to the previous screen/state, and if the current direction is left, I set it to right, else, I don't do anything. This main controller is added in the Body tag. This is what happens when on the enrollments.enrollment state, and going back up to the enrollments.list state.

<div class="scroller ng-scope slide-left ng-animate ng-enter ng-enter-active" ui-view="" ng-class="slide" style=""><div ng-model="enrollment" class="ng-scope ng-pristine ng-valid">
<div class="scroller ng-scope slide-right ng-animate ng-enter ng-enter-active" ui-view="" ng-class="slide" style=""><div ng-model="enrollment" class="ng-scope ng-pristine ng-valid">

The problem of ng-class out of sync was solved by the ui-router team, but apparently I'm doing something wrong. I also don't like my current workaround to fetch every click to navigate deeper in the app.

So: how can I use slide-left/right with the back button and keep the classes in sync?

Territorialism answered 25/8, 2014 at 21:51 Comment(2)
Did you find a solution? I am facing a similar roadblock right now. ThanksYeisk
No, not really, but the project that contains this issue is set on hold. However, I would suggest (if feasible) to use ionic for mobile/app projects, I love it! So much good stuff already built in!Territorialism
D
1

I have try my best

 var myapp = angular.module('myApp', ["ui.router", "ngAnimate", 'hmTouchEvents', 'ngSanitize'])
myapp.config(function($stateProvider, $urlRouterProvider, $animateProvider){   
    $animateProvider.classNameFilter(/ani-/);
    $stateProvider
        .state('anon',{
            template:'<ui-view class="ani-ui-view" ng-class="abstractView"/>',
            abstract:true
        })
        .state("anon.foo", {
            url: "/foo",
            templateUrl : '/views/statechange/resources/partials/foo.html',
            controller : 'fooCtl'
        })
        .state("anon.bar", {
            url: "/bar",
            templateUrl : '/views/statechange/resources/partials/bar.html',
            controller : 'barCtl'
        });

    $urlRouterProvider.otherwise("/foo");
})
.run(function($rootScope){
    $rootScope.$on("$stateChangeStart", function(event, currRoute, prevRoute, rejection) {

    });
})
.animation('.fade-in', function($timeout){
    return {
        enter : function(element, done){
            element.css({
                'opacity':0,
                transition:'all 300ms'
            });
            $timeout(function(){
                element.css({
                    'opacity':1
                });
            },0)
        }
    }
})
.animation('.show-bar', function($timeout) {
    return {
        enter : function(element, done) {
                element.css({
                    transition:'all 300ms',
                    transform:'translate3d(100%, 0, 0)'
                });

                $timeout(function(){
                    element.css({
                        transform:'translate3d(0, 0, 0)'
                    });
                },0);

            },
        leave : function(element, done) {
                element.css({
                    transition:'all 300ms',
                    transform:'translate3d(0, 0, 0)'
                });

                $timeout(function(){
                    element.css({
                        transform:'translate3d(100%, 0, 0)'
                    });
                },0);
            },

        // you can also capture these animation events
        addClass : function(element, className, done) {},
        removeClass : function(element, className, done) {}
    }
})
.animation('.slide-foo', function($timeout) {
    return {
        enter : function(element, done) {
                element.css({
                    transition:'all 300ms',
                    transform:'translate3d(-100%, 0, 0)'
                });

                $timeout(function(){
                    element.css({
                        transform:'translate3d(0, 0, 0)'
                    });
                },0);

            },
        leave : function(element, done) {
                element.css({
                    transition:'all 300ms',
                    transform:'translate3d(0, 0, 0)'
                });

                $timeout(function(){
                    element.css({
                        transform:'translate3d(100%, 0, 0)'
                    });
                },0);
            },

        // you can also capture these animation events
        addClass : function(element, className, done) {},
        removeClass : function(element, className, done) {}
    }
})

.controller('mainCtl',[
    '$scope', '$state', '$rootScope', '$window',
    function($scope, $state, $rootScope, $window){
        $rootScope.abstractView = 'fade-in';


        console.log('mainCtl');


    }
])
.controller('fooCtl',[
    '$scope', '$state', '$timeout', '$rootScope',
    function($scope, $state, $timeout, $rootScope){

        $scope.changeState = function(_state){
            $rootScope.abstractView = 'show-bar';
            $state.go('anon.bar');
        }

        $scope.tip="I am foo";
    }
])
 .controller('barCtl',[
    '$scope', '$state', '$stateParams', '$timeout', '$rootScope', '$window',
    function($scope, $state, $stateParams, $timeout, $rootScope, $window){
        $timeout(function(){
            $scope.barshow = true;
        });
        $scope.tip="I am bar";

        $scope.goBack = function($event){
            $rootScope.abstractView = 'show-bar';
            $window.history.back();
        }
    }
]);

index html

<body ng-controller="mainCtl">
<div class="ui-view-container">
    <div ui-view></div>        
</div>
</body>

foo html

<section ng-controller="fooCtl">
<div class="well">
    {{tip}}
    <div class="text-right">
        <button class="btn btn-default" hm-tap="changeState('anon.bar')">to bar -&gt;</button>
    </div>
</div>

bar html

<section ng-controller="barCtl">
<div class="well">
    <div class="text-left">
        <button class="btn btn-info" hm-tap="goBack($event);">&lt;- back</button>
    </div>
    {{tip}}
</div>

some css

.ui-view-container {
        position:relative;
    }
    .ani-ui-view{
        position: absolute;
        left: 0;
        top: 0;
        width:100%;
    }

I hope it can help you

Dracula answered 11/9, 2015 at 8:46 Comment(1)
Please elaborate any answer with some explanations. Just posting code without any text does not help others to decide if your solution could help them, too, or how your solution differs from others - just imagine four such answers on this question: Then all visitors would have to visually find the differencesHobble
G
0

So if you want some functionality on your state change,you use modulename.run() where you are using states, and in modulename.run() you can specify what all you want on your current state chage or url change.

Suppose your module is

var mod = angular.module('yourModuleName', ['ui.router']);
mod.config(['$stateProvider', '$urlRouterProvider',
function($stateProvider,$urlRouterProvider){
$stateProvider
        .state('enrollments', {
            abstract: true,
            url: '/tutorProcessEnrollments',
            template: '<div class="scroller" ui-view ng-class="slide" ></div>'
        })
        .state('enrollments.list', {
            url: '',
            templateUrl: 'views/tutorProcessEnrollments.list.html',
            controller: 'TutorEnrollmentsCtrl'
        })
        .state('enrollments.enrollment', {
            url: '/:enrollment_id',
            templateUrl: 'views/tutorProcessEnrollments.enrollment.html',
            controller: 'TutorEnrollmentCtrl'
        })
}]);
mod.run(function($rootScope) {
        $rootScope.$on("$stateChangeStart", function(event, currRoute, prevRoute, rejection) {
            $rootScope.$emit('cancelTravellerPopUpTimer');
            $rootScope.$emit('closeTravellerInfoPopup');
        });
    });
Gamut answered 26/3, 2015 at 17:56 Comment(0)
G
0

I had the same problem but I had only three screens, but maybe with some tweaks it will result in the desired effect.

So first things first:

<span ng-class="navReverse ? 'navleft' : 'navright'">
  <div ui-view="nav"></div>
</span>

You have to add the ng-class before the ui-view. Else it will add the new template with the current class instead of the desired class.

Then in your parent controller you can define logic for transitions. For me this was something like:

$transitions.onCreate({}, function($transition) {
    if ($transition.$from().name == 'a' && $transition.$to().name == 'a.b') {
      $scope.navReverse = false;
    }
    if ($transition.$from().name == 'b' && $transition.$to().name == 'a') {
      $scope.navReverse = true;
    }
}

You can probably adjust this somehow if it needs to apply to more screens. Like checking for depth of the routes.

Gastroenterology answered 19/12, 2018 at 9:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.