$stateProvider 里面的 resolve 属性 执行之前需要解析一个 $http 请求

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

我正在构建一个 angular 应用程序,它将在多个域上 运行。由于每个域都有不同的配置,我需要通过调用服务器来获取所有变量。该调用将 return 一个包含不同 rest url 的 JSON 对象。 我的问题是我需要在 $stateProvider 中的 'resolve' 步骤之前执行此调用,因为我已经有一个任务依赖于来自服务器的配置对象。

这里应该工作的是一个非常棒的功能 $urlRouterProvider.deferIntercept(); 记录在这里:

$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.

API 文档中的代码片段:

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();
});

另外,与这个问题相关:

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

working example - plunker

为了说清楚,适合这个用例,让我们观察一下plunker.

的代码

所以,首先我们可以看到.config()阶段。它确实可以访问提供者,但不能访问他们的服务(例如 $http)。还没有,服务本身将在稍后提供...

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;
});

我们所做的是设置对提供程序 (可配置对象) 的引用,以供稍后使用:$stateProviderRef.

最关键的是我们停止了 UI-Router,并强迫他用 $urlRouterProvider.deferIntercept(); 等待我们(参见上面的 doc 和引用)

.run()阶段的摘录:

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();
      });
}]);

最重要的是,这个 .run() 只执行了 ONCE。只有一次。正如我们所要求的。

我们还可以使用另一种技术:在一个超级根状态内部解析,它是所有状态层次结构根的父级。在此处查看所有详细信息:

还有另一种方法可以解决:

How to resolve $http request before the execution of the resolve property inside $stateProvider?

如果我们只需要在 resolve 中得到一些 $http 结果,我们可以这样做:

resolve: {
    myResolve1:
        function($http, $stateParams) {
            return $http.get("/api/foos/"+stateParams.fooID);
        }
    }

这是 [$stateProvider][1] 部分 resolve 的文档片段。我们可以看到,我们 return $http 服务的承诺:return $http.get()

因此,将其扩展为答案:

But how can I make the ui-router wait until the promise is resolved?

我们可以只使用 return $http.get() 然后 .then()。在其中,我们可以访问 returned 结果——我们可以对其进行调整:

myResolve1:
    function($http, $stateParams) {
        // we still return the promise of the $http.get))
        return $http
          .get("/api/foos/"+stateParams.fooID)
          .then(function(response) {

            // but now, the content of resolved property
            //  will be the first item of the loaded array
            return response.data[0];
          };
     }
}

还有增强型解决方案 - 以防我们需要在每个状态之前实现这一点。我们只是引入一些 "root" 状态作为超级父级。它将包含这样的解决,所有子状态将等到这个解决,但只解决一次。在此处查看更多信息:angular ui-router resolve for parent state