Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Need to resolve a $http request before the execution of the resolve property inside $stateProvider

I’m building an angular application that is going to run on several domains. Since there are different configurations on each domain I'll need to fetch all the variables by doing a call to the server. The call will return a JSON object that contains different rest urls. My problem is that I need to do this call before the 'resolve' step inside the $stateProvider, since I already have a task that is dependent on the configuration object from the server.

like image 742
Christian Avatar asked Mar 21 '15 10:03

Christian


1 Answers

What should work here is a really great feature $urlRouterProvider.deferIntercept(); documented here:

$urlRouterProvider

The deferIntercept(defer)

Disables (or enables) deferring location change interception.

If you wish to customize the behavior of syncing the URL (for example, if you wish to defer a transition but maintain the current URL), call this method at configuration time. Then, at run time, call $urlRouter.listen() after you have configured your own $locationChangeSuccess event handler.

The code snippet from the API documentation:

var app = angular.module('app', ['ui.router.router']);

app.config(function($urlRouterProvider) {

  // Prevent $urlRouter from automatically intercepting URL changes;
  // this allows you to configure custom behavior in between
  // location changes and route synchronization:
  $urlRouterProvider.deferIntercept();

}).run(function($rootScope, $urlRouter, UserService) {

  $rootScope.$on('$locationChangeSuccess', function(e) {
    // UserService is an example service for managing user state
    if (UserService.isLoggedIn()) return;

    // Prevent $urlRouter's default handler from firing
    e.preventDefault();

    UserService.handleLogin().then(function() {
      // Once the user has logged in, sync the current URL
      // to the router:
      $urlRouter.sync();
    });
  });

  // Configures $urlRouter's listener *after* your custom listener
  $urlRouter.listen();
});

And also, related to this question:

  • AngularJS - UI-router - How to configure dynamic views

There is working example - plunker

To make it clear, suitable for this use case, let's observe the code of the plunker.

So, firstly we can see the .config() phase. It does have access to providers but NOT to their services (e.g. $http). Not yet, services themselves will be available later...

app.config(function ($locationProvider, $urlRouterProvider, $stateProvider) 
{
    // this will put UI-Router into hibernation
    // waiting for explicit resurrection later
    // it will give us time to do anything we want... even in .run() phase
    $urlRouterProvider.deferIntercept();
    $urlRouterProvider.otherwise('/other');
    
    $locationProvider.html5Mode({enabled: false});
    $stateProviderRef = $stateProvider;
});

What we did, is set a reference to provider (configurable object), to be used later: $stateProviderRef.

And the most crucial thing is we STOPPED the UI-Router, and forced him to wait for us with $urlRouterProvider.deferIntercept(); (see the doc and cites above)

There is an extract of the .run() phase:

app.run(['$q', '$rootScope','$http', '$urlRouter',
  function ($q, $rootScope, $http, $urlRouter) 
  {

   // RUN phase can use services (conigured in config phase)
   // e.g. $http to load some data
    $http
      .get("myJson.json")
      .success(function(data)
      {

        // here we can use the loaded stuff to enhance our states
        angular.forEach(data, function (value, key) 
        { 
          var state = { ... }
          ...

          $stateProviderRef.state(value.name, state);
        });
        // Configures $urlRouter's listener *after* your custom listener
        
        // here comes resurrection of the UI-Router
        // these two important calls, will return the execution to the 
        // routing provider
        // and let the application to use just loaded stuff
        $urlRouter.sync();
        $urlRouter.listen();
      });
}]);

Most important is, that this .run() was executed just ONCE. Only once. As we require.

We can also use another technique: resolve inside of one super root state, which is parent of all state hierarchy roots. Check all the details here:

Nested states or views for layout with leftbar in ui-router?

like image 50
Radim Köhler Avatar answered Nov 15 '22 03:11

Radim Köhler