christopherthielen / ui-router-extras

THIS PROJECT IS NO LONGER MAINTAINED -- Extras for UI-Router for AngularJS. Sticky States (a.k.a. parallel states), Deep State Redirect (for tab-like navigation), Future States (async state definition)
http://christopherthielen.github.io/ui-router-extras/
MIT License
917 stars 211 forks source link

how to preload states - revisited #363

Closed nirbil closed 7 years ago

nirbil commented 7 years ago

I'm using ui-router-extras for a project I'm working on.

I know this has been asked in the past (#238) but I have some problems with the suggested solution.

I'm trying to pre-load some states but I need to keep them hidden until they actually kick in. In my current architecture, states are revealed and hidden by a designated directive that is tracking $stateChangeStart and $stateChangeSuccess.

As suggested by @christopherthielen , I've added preload to the relevant routes that need to be preloaded and adjusted the app bootstrap like this:

app.config(...
    $urlRouterProvider.deferIntercept();
    ...
});
app.run(...
    $state.get()
    .filter(function(state) {
      return state.preload;
    })
    .reduce(function (memo, state) {
      return memo.then(function() {
        return $state.go(state, undefined, { location: false });
      });
    }, $q.when())
    .then(function() {
      $urlRouter.listen();
      $urlRouter.sync();
      if ($state.transition == null) {
        $rootScope.statesPreloaded = true;
      }
      else {
        $state.transition.then(function() {
          $rootScope.statesPreloaded = true;
        });
      }
    });
    ...
});

Just to clarify - in addition to the suggested solution, I've added statesPreloaded (wish I didn't have to...) to the rootScope, and I'm maintaining it after the sync and after I figure out if another transition is due.

The reason for doing so is because after the sync I get a stateChangeSuccess event to the preloaded state, and right after that a stateChangeSuccess to the final state, which resulted in a flickering of the preloaded state... I'm no expert with ui-router, but it seemed odd to be getting that first event, was not expecting that.

This all works great except for one use case (more likely several): If I go to a an undefined route (like going to my base url), this leads to the 'otherwise' route in the routeProvider, which leads to my default state. In such case after the sync there is no transition pending on the $state so the statesPreloaded is marked as true and then the flicker again...

I'm thinking that there must be cleaner way of doing this... For instance - is there a way, after the preloading to change the current state back to its initial state?

Would love some input! Thanks.

nirbil commented 7 years ago

Ended up doing this after the sync:

      $urlRouter.listen();
      $urlRouter.sync();
      if ($state.$current.url.exec($location.url()) != null) {
        $rootScope.statesPreloaded = true;
      }

      else {
        var unregFunc = $rootScope.$on("$stateChangeSuccess", function() {
          $rootScope.statesPreloaded = true;
          unregFunc();
        });
      }

If the current url matches the preloaded state then everything's ready. if not, wait for a single state change.