Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best practice for animating multiple ui-views as one

I have an app that basically looks like this

<div ui-view="header">
<div ui-view="main">
<div ui-view="footer">

Now, the footer will stay the same for all different states of the app, but the header will change in some states, but also share content in a lot of the states. The only ui-view that will change across all states is ui-view="main".

Currently my $stateProvider looks like this (footer not implemented yet):

app.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {
  $stateProvider.state('root',{
      url: '',
      abstract: true,
      views: {
        'header': {
          templateUrl: appHelper.views+'/header.html',
        },
      }
    })
    .state('root.home', {
      url: '/',
      views: {
        'header': {
          templateUrl: appHelper.views+'/header.html',
        },
        'main@': {
          templateUrl: appHelper.views+'/home.html',
          controller: 'HomeController as homeVm',
        }
      },
      resolve: {
        posts: function(dataService) {
          return dataService.getPosts();
        },
        artists: function(dataService) {
          return dataService.getArtists();
        }
      }
    })
    .state('root.artist', {
      url: '/artist/:slug',
      views: {
        'header@': {
          templateUrl: appHelper.views+'/artistHeader.html',
          controller: 'ArtistController as artistVm',
        },
        'main@': {
          templateUrl: appHelper.views+'/artist.html',
          controller: 'ArtistController as artistVm',
        }
      },
      resolve: {
        artist: function($stateParams, dataService) {
          return dataService.getArtist($stateParams.slug);    
        }
      }
    });
}]);

So far, so good. But here’s the catch. During transitions I want to animate all the ui-views as one. To make things look cohesive in my case (it’s a fade animation) I would like to put them in a wrapping div and have that div do the fade out/fade in (doing it on each different ui-view will cause all kinds of ugliness).

What is best practice in this scenario?

Do I wrap them in a ui-view and hook that up in the $stateProvider somehow (nested ui-views?). Are there any other ways to do it? Can I listen to $stateChange and apply classes to my wrapper like ngAnimate would with an ui-view? (fade out then wait until entirely faded out with a successful resolve before fading in).

From my googling efforts it seems ui-view is much preferred when handling animations of the transition type.

like image 943
INT Avatar asked Jun 24 '15 17:06

INT


1 Answers

... During transitions I want to animate all the ui-views as one. To make things look cohesive in my case (it’s a fade animation) I would like to put them in a wrapping div and have that div do the fade out/fade in (doing it on each different ui-view will cause all kinds of uglyness).

What is best practice in this scenario?

Option 1: Using event hooks $stateChangeStart and $stateChangeSuccess

This approach would have some side effects as @David mentioned in comments. So avoid it until ui-router comes up with a better way to manage state transition promise. Currently it relies on event.preventDefault() to prevent the transition from happening.

Basically, ui-router will fire your ui-view route change view-out at the same time as the incoming view-in. So what you get is the previous ui-view still visible while the next ui-view is rendered and if you're animating the view-out then there will be that transition duration of overlap between the old view and the new. You should look into holding off on rendering the new ui-view until the old one is finished animating out (before animating the new one in)

For similar reasons, wrapping them in another ui-view and hooking them up in the $stateProvider would not be wise.

Option 2:(Recommended) CSS based animations

I mostly go with CSS based transitions and you may achieve this either by

  • assigning a common CSS class to each ui-view and applying animation on that class.
  • or enclosing the three ui-view in a <div>(if you really need this) and apply animation on it.

Either way, you would be treating all ui-views as a single entity and animating them would make things look more cohesive.

HTML:

<div ui-view="header" ></div>    
<div ui-view="main" ></div>
<div ui-view="footer" ></div>

CSS:

[ui-view].ng-enter { 
   animation: fadein 2s;
}

@keyframes fadein {
    from { opacity: 0; }
    to   { opacity: 1; }
}

Here's a working plunkr based on your use case where, I have an abstract state root and three nested states root.home, root.artist and root.band. While the ui-view='main' gets changed across all states, ui-view='footer' doesnt change in any and ui-view='header' changes for the state 'root.artist'. The fact that you are using css based .ng-enter and .ng-leave classes to trigger the animations, you technically overcome the issues with previous approach.

  $stateProvider
        .state('root',{
          abstract:true,
            views:{
              'header':{  //absolutely targets the 'header' view in root unnamed state.
                templateUrl:'header.html'
              },
              'footer':{  //absolutely targets the 'footer' view in root unnamed state.
                templateUrl:'footer.html'
              }
            }
        })
        .state('root.home',{
            views:{
              'main@':{
                templateUrl:'main.html'
              },
            }          
        })
        .state('root.artist',{
            views:{
              'header@':{
                templateUrl:'artist-header.html'
              },
              'main@':{
                templateUrl:'artist-main.html'
              },
            }          
        })
        .state('root.band',{
            views:{
              'main@':{
                templateUrl:'band-main.html'
              },
            }          
        })
like image 70
nalinc Avatar answered Oct 22 '22 05:10

nalinc