我有一项服务,我想通过传递一个函数来配置它来使用它。只要此函数返回我的服务可以使用的内容,它就不关心它如何获取数据,也不关心同步/异步。以下是我希望能够做的事情的样本,但由于显而易见的原因无效:
.config(function(MyServiceProvider, OtherService) {
MyServiceProvider.setSomeMethod( OtherService.someMethod );
})
那会很棒,但似乎没有一种方法可以引用" OtherService"来自配置功能。我知道这是因为OtherService
可能尚未配置,因此它的实例还不存在,但那么在这种情况下该做什么呢?
(有焦虑)是否适合在运行区内进行此关联?
.run(function(MyService, OtherService) {
MyService.setSomeMethod( OtherService.someMethod );
})
答案 0 :(得分:1)
使用$injector服务稍后解决依赖关系,当引导程序已经解决时。
例如:
angular.module('myApp').config(function(myProvider) {
myProvider.setMethod('myUberMethod');
});
// Somewhere in myProvider
var theUberMethod, theUberMethodName;
function setMethod(dependencyName) {
theUberMethodName = dependencyName;
}
function performTheMethod() {
theUberMethod = theUberMethod || $injector.get(theUberMethodName);
// Magic
theUberMethod();
}
您甚至可能想看一下 $ injector 的 invoke 方法,因为这样可以将参数注入到被注入的函数方法中。 / p>
我必须更好地解释它。
您遇到的问题与注入模块未在配置阶段初始化的事实有关,因此您无法将服务注入配置功能。仅允许提供商。在配置阶段之后,你必须推迟任何依赖解析。这可以通过在提供程序上使用$ get函数或注入$ injector服务并使用它来进行解析来完成。
可以像任何其他对象一样注册函数以进行依赖注入。因此,为提供者提供函数的名称就足以让它在以后解析并执行它。
myApp.provider('myFunctionCallee', function() {
var self = this;
return {
hookMyFunction: function(value /* string: name of the function */ ) {
self.myFunction = value;
},
$get: function($injector) {
return {
executeMyFunction: function() {
return $injector.get(self.myFunction)();
}
};
}
};
});
现在我们需要一种方法来通知注射器该功能。
这是最简单的......只需用注射器直接注册一个函数(对象)。我们将通过请求'myFunction'来解决该问题。
myApp.value('myFunction', function() {
return 'hello injected function!';
});
myApp.config(function(myFunctionCalleeProvider) {
myFunctionCalleeProvider.hookMyFunction('myFunction');
});
这种方法看起来很像前一种方法。我们将直接注册一个函数,但我们也会在目标服务中注入它。
myApp.value('myFunction', function() {
return 'hello injected service function 1';
});
myApp.factory('myService', function(myFunction) {
return {
myServiceFunction: myFunction
};
});
myApp.config(function(myFunctionCalleeProvider) {
myFunctionCalleeProvider.hookMyFunction('myFunction');
});
有时候以前的方法是不可能的。也许代码是由其他人编写的,他/她没有这个伟大的愿景。但是,我们可以在任何可注入对象上公开任何函数:
myApp.factory('myService', function() {
return {
// We want to target this beast
myServiceFunction: function() {
return 'hello injected service function 2';
}
};
});
// Inject the target service here ...
myApp.factory('myFunction', function(myService) {
// ... and expose the beast.
return myService.myServiceFunction;
});
myApp.config(function(myFunctionCalleeProvider) {
myFunctionCalleeProvider.hookMyFunction('myFunction');
});
你可以在plunker中看到,感受和舔它。
答案 1 :(得分:1)
这两个答案都是@ null的答案的衍生物。为了完整和清晰起见,我保留这里。我不认为这个话题在其他地方已经得到了很好的讨论,而且可能很难让人满脑子(就像我的情况一样)。
在下面我不太理想的第二种方法中,我使用$q.when()
来包装作为主题的已配置函数。我建议在这种情况下始终使用它,以便配置的功能可以安全地同步或异步。为了降低复杂性,我将其从第一个例子中删除。
使用引用其他服务的字符串名称配置提供程序 - 这种方式可能不是您实际尝试访问的服务:
.config(function(SomeServiceProvider) {
SomeServiceProvider.someServiceName = 'ThisService';
})
ThisService
将被SomeService
称为函数。 ThisService
可以将多个其他服务注入其中,对它们执行某些操作并返回结果。
.factory('ThisService', function(FooService, BarService) {
var service = function() {
var x = FooService.whatever();
var y = BarService.blah();
return x+y;
};
return service;
})
在您配置的SomeService
中,使用$injector
获取ThisService
。
.provider('SomeService', [
function() {
var config = this;
this.someServiceName = null;
this.$get = [
'$injector',
factory
];
function factory($injector) {
var service = function() {
//do stuff
//call the configured function
var fn = $injector.get(config.someServiceName);
var myData = fn();
//etc
};
return service;
}
}
])
我目前正在通过使用默认函数扩展这一点,如果在配置中没有给出服务名称,将会调用该函数:
.provider('SomeService', [
function() {
var config = this;
this.defaultFn = function() {
//default stuff
};
this.someServiceName = null;
this.$get = [
'$injector',
factory
];
function factory($injector) {
var service = function() {
//do stuff
//use defaultFn if no service name was given
var fn = config.someServiceName
? $injector.get(config.someServiceName)
: config.defaultFn
;
var myData = fn();
//etc
};
return service;
}
}
])
这种方法的一个小缺点就是它创建了另一个命名的角度组件(服务),但这确实不是问题,除非你在命名或非常不幸的情况下非常糟糕。我只是将“Config”附加到服务名称的开头,以对命名空间进行排序并对其进行标记。
这是我的老答案,我赞成上述解决方案。
(这里使用$q
与问题无关。我将其包含在内,因为它与功能非常相关 - 配置的服务可以使用同步或异步功能。)
.config([
'MyServiceProvider',
function(MyServiceProvider) {
MyServiceProvider.setSomeMethod(function($injector) {
var OtherModule = $injector.get('OtherModule');
OtherModule.someMethod();
});
}
])
.provider('MyService', [
function() {
var config = this;
this.someMethod = function() { };
this.setSomeMethod = function(fn) {
this.someMethod = fn;
};
this.$get = [
'$q',
'$injector',
factory
];
function factory($q, $injector) {
var service = function() {
var loaded = config.someMethod($injector);
$q.when(loaded).then(function() {
});
};
return service;
}
}
])