AngularJS UI路由器在工厂/服务中使用已解析的依赖关系

时间:2015-01-11 19:24:51

标签: javascript angularjs angular-ui-router

我有一个UI路由器定义类似这样(为简单起见修剪):

    $stateProvider
        .state('someState', {
            resolve: {
                model: ['modelService', 'info', function (modelService, info) {
                    return modelService.get(info.id).$promise;
                }]
            },
            controller: 'SomeController'
        });

someState状态正在使用依赖于model解析的工厂/服务。它的定义是这样的,AngularJS在这里抛出未知提供者:modelProvider< - model< - someService 错误:

angular
    .module('someModule')
    .factory('someService', someService);

someService.$inject = ['model'];
function someService(model) { ... }

但是,在此状态的控制器内使用相同的model解析可以正常工作

SomeController.$inject = ['model'];
function SomeController(model) { ... }

所以我理解UI路由器正在延迟SomeController的DI,直到解决方案发生,这允许AngularJS不会抛出错误。但是,如果将该解决方案作为someService的依赖关系,怎么会发生同样的延迟呢?解析只能在控制器上工作吗?如果是这种情况,我如何在工厂/服务中使用解决方案?

4 个答案:

答案 0 :(得分:14)

  

只做解决方案吗?

是的,只能在控制器上解析。

  

如果是这种情况,我如何在工厂/服务中使用解决方案?

请记住,工厂和服务返回 singleton 对象,即第一次将工厂注入控制器时,它会运行您提供的任何实例化代码并创建一个对象,然后是工厂的任何后续时间实例化,返回相同的对象。

换句话说:

angular.module('someModule')
.factory( 'SomeFactory' , function () {
  // this code only runs once
  object = {}
  object.now = Date.now();
  return object
);

SomeFactory.now将是工厂首次注入控制器的当前时间,但会在后续使用时更新。

因此,工厂决心的概念并不真正有意义。如果你想拥有一个动态做某事的服务(这显然很常见),你需要把逻辑放在单例的函数内。

例如,在您提供的代码示例中,您的工厂依赖于模型。一种方法是使用您已经设置的解析方法将模型注入控制器,然后在接受模型的单例上公开一个方法并执行您需要执行的操作,如下所示:

angular.module('someModule')
.factory( 'SomeFactory', function () {
  return {
    doSomethingWithModel: function (model) {
      $http.post('wherever', model);
  }
});
.controller('SomeController', function (SomeFactory, model) {
  SomeFactory.doSomethingWithModel(model);
});

或者,如果您根本不需要控制器中已解析的值,请不要直接将其解析,而是将解析逻辑放入服务单例的方法中并在resolve中调用该方法,将结果传递给控制器​​。

很难通过抽象对话更详细,所以如果你需要进一步的指示,那么提供一个特定的用例。

答案 1 :(得分:3)

只是为了补充Ed Hinchliffe的回答,还有一件事你可以试试。正如其他人所提到的,工厂和服务都是单身人士。然而,我们可以使用服务和工厂之间的一个关键区别,服务提供服务功能的实例(new ServiceFunction()),而工厂提供通过调用传递给的函数引用返回的值。模块。此信息在此stackoverflow线程中进一步说明:

service-vs-provider-vs-factory

所以,基本上这意味着我们可以在工厂上创建一个函数,向其原型添加属性,然后在控制器上创建一个实例,传递我们喜欢的参数。这是一个非常基本的例子:

假设我们在全局app变量上有我们的角度模块。首先,我们使用resolve属性创建Ui-Router状态:

app.config(function ($stateProvider) {
$stateProvider
  .state('example', {
    url: '/example',
    templateUrl: 'app/example/example.html',
    controller: 'ExampleCtrl',
    resolve: {
        repeatValue: ['$q', '$timeout', function($q, $timeout){
            var deferred = $q.defer();
            $timeout(function(){
                deferred.resolve( parseInt(Math.random() * 100) );
            }, 3000);
            return deferred.promise;
        }]
    }
  });
});

为了表示异步操作,我们使用角度核心提供的$q$timeout服务在reslove对象上返回一个promise,它将在三秒后解析。

现在我们需要使用之前解释的方法创建我们的工厂:

app.factory('Greeter', function () {
// Function
var Greeter = function(repeat){
  this.repeat = repeat;
};

// Prototype
Greeter.prototype.repeatedHi = function() {
  var array = [];
  console.log(this);
  for (var i = 0; i < this.repeat; i++){
    array.push('Hi');
  }
  return array;
};

// Public API here
return Greeter;
});

这里我们创建了Greeter工厂作为构造函数。我们现在可以实例化这个工厂传递我们喜欢的参数。在此示例中,我们需要为构造函数提供repeat值。如果我们想向工厂注入异步值,利用ui-state的resolve属性,我们可以在控制器上执行此操作:

app.controller('ExampleCtrl', function ($scope, Greeter, repeatValue) {
    $scope.repeatValue = repeatValue;

    var greeter = new Greeter(repeatValue);

    $scope.greetingsArray   = greeter.repeatedHi();
});

$scope.greetingsArray将填充repeatValue个“Hi”字符串。

我希望自己清楚明白,希望有所帮助。

答案 2 :(得分:1)

您不能在服务或工厂中使用已解析的值,只能在与已解析值属于同一状态的控制器中使用。服务和工厂是单例,控制器是新实例化的(在这种情况下)是一个州或任何使用ng-controller的地方。

实例化发生在$ controller服务上,该服务能够注入仅属于该控制器的对象。服务和工厂没有这种能力。

答案 3 :(得分:1)

您应该查看依赖注入的angular docs

  • 服务,指令,过滤器和动画等组件由可注入工厂方法或构造函数定义。 这些组件可以注入&#34; service&#34;和&#34;价值&#34;组件作为依赖项。
  • 控制器由构造函数定义,可以注入任何&#34; service&#34;和&#34;价值&#34;组件作为依赖项,但它们也可以提供特殊的依赖项。请参阅下面的控制器以获取这些特殊依赖项的列表。
  • run方法接受一个函数,该函数可以注入&#34; service&#34;,&#34; value&#34;和&#34;常数&#34;组件作为依赖项。请注意,您不能注入&#34;提供商&#34;进入跑步区。
  • config方法接受一个函数,可以使用&#34; provider&#34;和&#34;常数&#34;组件作为依赖项。请注意,您无法注入&#34; service&#34;或&#34;价值&#34;组件进入配置。

因此每种角度组件都有自己的可接受组件列表。由于服务是单身,因此注入价值作为决心的一部分真的没有任何意义。如果您的页面上有两个单独的位置使用具有不同结果的服务,则结果将是不确定的。将$scope注入您的服务没有任何意义。它对控制器有意义,因为控制器负责正在解析的页面的相同区域。

如果你的someService需要使用模型中的数据,它应该有一个函数将数据作为参数,你的控制器应该传递它。