ui-router: Don't wait till final child state is done being configured in order to load parent templates and controllers

In my app, I have a root state with a menu bar, and a root.ticket state as it’s child. I am dynamically resolving the root.ticket template using a templateProvider which makes an ajax call to get the template. If this resolution is slow, the entire screen is blank until the ajax call fails or succeeds. It would be nice if the parent states’ controllers and templates were loaded before the child finished. That would let me at least display the menu bar located in one of the views in root state while the child template was loading.

Similar issue: http://stackoverflow.com/questions/18961332/angular-ui-router-show-loading-animation-during-resolve-process

About this issue

  • Original URL
  • State: closed
  • Created 10 years ago
  • Reactions: 4
  • Comments: 21 (7 by maintainers)

Most upvoted comments

For anyone interested in solving their similar issues… here is how I ended up tricking the router to do basically what I wanted, and without having to actually modifying my route configs.

⚠️ Disclaimer: this is still an hack, it works® tho.

Preconditions: I have a lot of nested states, which have a lot of resolves (for data fetching, lazy loading modules, etc)… however, the first parent state doesn’t have any resolve, never: it’s just a template that renders like an app layout with an <ui-view>:

$stateProvider
    .state('myState', {
      // e.g. the app layout
      template: '<header/><ui-view>Loading…</ui-view><footer/>',
    })

    .state('myState.child', {
      url: '/my-url',
      template: '<my-component></my-component>',
      resolve: {
        // e.g. async functions which may take a while
        load: importModule('whatever'),
        fetch: fetchData('whatever'),
      },
    })

So far so good. The problem comes when from another part of the app we want to link to the myState.child; the router will not render anything until everything has been resolved. Result? The app seems stuck, no Loading… indicator. :feelsgood:

Solution? Create a function that first enters the parent state (so the spinner will appear, no resolves), then, once succeeded, it redirects to the original state:

yourModule
  // rename it to whatever you prefer
  .factory('hackedState', ($state, $rootScope) => {

    'ngInject';

    return {
      // HACK: part 1
      go(stateName, stateParams) {
        // save the current state
        $rootScope.pendingState = {
          name   : stateName,
          params : stateParams,
        };
        // go to the parent state
        return $state.go(stateName.split('.')[0]);
      },
    };
  })
  .run(($state, $rootScope) => {
    
    'ngInject';

    $rootScope.$on('$stateChangeSuccess', (event, next, nextParams) => {
      // HACK: part 2
      if ($rootScope.pendingState) {
        const { name, params } = $rootScope.pendingState;
        $rootScope.pendingState = undefined;
        // clean the pending state and transition to it
        return $state.transitionTo(name, params, {
          location: 'replace',
          relative: next,
        });
      }
    });

  });

Now you can use hackedState.go('myState.child', { /*params*/ }) and the loading indicator will appear.

:suspect: