我有一个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
的依赖关系,怎么会发生同样的延迟呢?解析只能在控制器上工作吗?如果是这种情况,我如何在工厂/服务中使用解决方案?
答案 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:
因此每种角度组件都有自己的可接受组件列表。由于服务是单身,因此注入价值作为决心的一部分真的没有任何意义。如果您的页面上有两个单独的位置使用具有不同结果的服务,则结果将是不确定的。将$scope
注入您的服务没有任何意义。它对控制器有意义,因为控制器负责正在解析的页面的相同区域。
如果你的someService
需要使用模型中的数据,它应该有一个函数将数据作为参数,你的控制器应该传递它。