Angular Run Block - 使用UI-Router $ stateProvider来解决Promise

时间:2015-11-16 15:59:06

标签: javascript angularjs angular-ui-router

UI-RouterAngular的{​​{1}}不同。它支持普通ngRoute可以执行的所有操作以及许多额外功能。

我正在将ngRoute应用从Angular更改为ngRoute。但我无法弄清楚如何以编程方式注入UI-Router函数 - 我在resolveController之外使用的一段代码。

因此,使用标准config的{​​{1}}我可以在Angular运行块中动态注入ngRoute

resolve promise

现在如何使用Angular以类似的方式注入解决方案承诺?我尝试通过app.run(function ($route) { var route = $route.routes['/']; route.resolve = route.resolve || {}; route.resolve.getData = function(myService){return myService.getSomeData();}; }); 来访问UI-Router,但这对我来说失败了。

$stateProvider

4 个答案:

答案 0 :(得分:6)

您可以使用resolve在控制器加载下一个状态之前为其提供数据。要访问已解析的对象,您需要将它们作为依赖项注入控制器。

我们以购物清单应用程序为例。我们首先定义我们的应用程序模块,并将ui.router作为依赖项。:

angular.module('myApp', ['ui.router']);

我们现在想要定义特定于我们应用程序的购物清单页面的模块。我们将定义一个shoppingList模块,包括该模块的状态,该状态的解析以及控制器。

购物清单模块

angular.module('myApp.shoppingList').config(function ($stateProvider) {

    $stateProvider.state('app.shoppingList', {
        url: '/shopping-list',
        templateUrl: 'shopping-list.html',
        controller: 'ShoppingListController',
        resolve: {
            shoppingLists: function (ShoppingListService) {
                return ShoppingListService.getAll();
            }
        }
    });

});

我们现在可以将已解析的对象作为依赖项注入控制器。在上面的状态中,我正在解析一个名为shoppingLists的对象。如果我想在我的控制器中使用这个对象,我将它包含为具有相同名称的依赖项。

购物清单控制器

angular.module('myApp.shoppingList').controller('ShoppingListController', function ($scope, shoppingLists) {
    $scope.shoppingLists = shoppingLists;
});

有关其他详细信息,请阅读Angular-UI Wiki,其中包含in-depth guide to using resolve

答案 1 :(得分:0)

查看文档:

Resolve

  

您可以使用resolve为控制器提供自定义状态的内容或数据。 resolve是一个可选的依赖关系图,应该注入控制器。

     

如果这些依赖项中的任何一个是promise,它们将在实例化控制器并触发$ stateChangeSuccess事件之前被解析并转换为值。

     

resolve属性是一个地图对象。 map对象包含键/值对:

     
      
  • key - {string}:要注入控制器的依赖项的名称。
  •   
  • factory - {string | function}:      
        
    • 如果是字符串,则它是服务的别名。
    •   
    • 否则,如果是函数,则将其注入,并将返回值视为依赖项。如果结果是承诺,则解决   在实例化控制器并将其值注入之前   控制器。
    •   
  •   
     

示例:

     

在实例化控制器之前,必须解析下面解决的每个对象(如果它们是承诺,则通过deferred.resolve())。注意每个解析对象如何作为参数注入控制器。

     

州的示例代码

$stateProvider.state('myState', {
  resolve:{

     // Example using function with simple return value.
     // Since it's not a promise, it resolves immediately.
     simpleObj:  function(){
        return {value: 'simple!'};
     },

     // Example using function with returned promise.
     // This is the typical use case of resolve.
     // You need to inject any services that you are
     // using, e.g. $http in this example
     promiseObj:  function($http){
        // $http returns a promise for the url data
        return $http({method: 'GET', url: '/someUrl'});
     },

     // Another promise example. If you need to do some 
     // processing of the result, use .then, and your 
     // promise is chained in for free. This is another
     // typical use case of resolve.
     promiseObj2:  function($http){
        return $http({method: 'GET', url: '/someUrl'})
           .then (function (data) {
               return doSomeStuffFirst(data);
           });
     },        

     // Example using a service by name as string.
     // This would look for a 'translations' service
     // within the module and return it.
     // Note: The service could return a promise and
     // it would work just like the example above
     translations: "translations",

     // Example showing injection of service into
     // resolve function. Service then returns a
     // promise. Tip: Inject $stateParams to get
     // access to url parameters.
     translations2: function(translations, $stateParams){
         // Assume that getLang is a service method
         // that uses $http to fetch some translations.
         // Also assume our url was "/:lang/home".
         return translations.getLang($stateParams.lang);
     },

     // Example showing returning of custom made promise
     greeting: function($q, $timeout){
         var deferred = $q.defer();
         $timeout(function() {
             deferred.resolve('Hello!');
         }, 1000);
         return deferred.promise;
     }
  },
  

示例控制器,使用上述 resolve 内容

  // The controller waits for every one of the above items to be
  // completely resolved before instantiation. For example, the
  // controller will not instantiate until promiseObj's promise has 
  // been resolved. Then those objects are injected into the controller
  // and available for use.  
  controller: function($scope, simpleObj, promiseObj, promiseObj2, translations, translations2, greeting){
      $scope.simple = simpleObj.value;

      // You can be sure that promiseObj is ready to use!
      $scope.items = promiseObj.data.items;
      $scope.items = promiseObj2.items;

      $scope.title = translations.getLang("english").title;
      $scope.title = translations2.title;

      $scope.greeting = greeting;
  }
})

答案 2 :(得分:0)

您无法访问$stateProvider中的run,我认为您在创建状态配置后不应该更改状态配置(并且您可能无法做到无论如何),为什么不在状态创建上添加resolve

答案 3 :(得分:0)

为了无条件地将resolve添加到一个或多个状态,应该使用抽象状态进行继承:

$stateProvider
.state('root', {
    abstract: true,
    resolve: {
        common: ...
    },
})
.state('some', {
    parent: 'root',
    ...
});

这是首选的方法,不需要黑客攻击。

至于UI路由器中等效的动态$route解析器,这是一个小问题。使用state方法注册状态时,它将在内部存储prototypically inherited from the definition,而不是仅仅分配给状态存储。

虽然稍后可以使用$state.get('stateName')获取定义,但它与路由器内部使用的对象不同。由于JS继承的工作原理,如果状态中存在resolve对象,它将没有区别,因此可以在那里添加新的解析器属性。但如果没有$state.get('stateName').resolve,那就是死路一条。

解决方案是修补state方法并将resolve对象添加到所有状态,因此可以在以后修改解析器集。

angular.module('ui.router.hacked', ['ui.router'])
.config(function ($stateProvider) {
  var stateOriginal = $stateProvider.state;
  $stateProvider.state = function (name, config) {
    config.resolve = config.resolve || {};
    return stateOriginal.apply(this, arguments);
  }
})

angular.module('app', ['ui.router.hacked']).run(function ($state) {
  var state = $state.get('some');
  state.resolve.someResolver = ...;
});

与任何其他黑客一样,它可能有陷阱并且往往会破裂。虽然这个非常坚固和简单,但它需要额外的单元测试,如果可以使用传统方法,则不应该使用它。