在Angular中调用具有依赖项的函数 [英] Invoking a function with dependencies in Angular
问题描述
使用Angular 5和UIRouter状态路由。我正在根据该接口使用其他自定义路由状态属性。
Using Angular 5 and UIRouter state routing. I'm using an additional custom route state property as per this interface.
interface AugmentedNg2RouteDefinition extends Ng2StateDeclaration {
default?: string | ((...args: any[]) => string | Promise<string>);
}
当我定义抽象状态时,现在可以添加 default
属性,因此当尝试将其路由到抽象状态时,默认属性会将其重定向到已配置的 default 子状态。
When I define an abstract state, I can now add a default
property to it as well, so when one would try to route to an abstract state, the default should redirect them to configured default child state.
从上面的界面可以理解,默认值
可以定义为以下任意值:
As can be understood from the interface above, the default
may be defined as any of the following:
// relative state name
default: '.child',
// absolute state name
default: 'parent.child',
// function with DI injectables
default: (auth: AuthService, stateService: StateService) => {
if (auth.isAuthenticated) {
return '.child';
} else {
return stateService.target('.login', { ... });
}
}
// function with DI injectables returning a promise
default: (items: ItemsService) => {
return items
.getTotal()
.then((count) => {
return count > 7
? '.simple'
: '.paged';
});
}
实际上使为默认值
工作,我必须配置路由转换服务:
To actually make the default
work, I have to configure route transition service:
@NgModule({
imports: [
...
UIRouterModule.forChild({ // or "forRoot"
states: ...
// THIS SHOULD PROCESS "default" PROPERTY ON ABSTRACT STATES
config: (uiRouter: UIRouter, injector: Injector, module: StatesModule) => {
uiRouter.transitionService.onBefore(
// ONLY RUN THIS ON ABSTRACTS WITH "default" SET
{
to: state => state.abstract === true && !!state.self.default
},
// PROCESS "default" VALUE
transition => {
let to: transition.to();
if (angular.isFunction(to.default)) {
// OK WE HAVE TO EXECUTE THE FUNCTION WITH INJECTABLES SOMEHOW
} else {
// this one's simple as "default" is a string
if (to.default[0] === '.') {
to.default = to.name + to.default;
}
return transition.router.stateService.target(to.default);
}
}
);
}
})
]
})
export class SomeFeatureModule { }
因此问题出在默认值
是一个函数中,它可能具有一些可注入的服务/值...
So the problem is invoking the default
when it's a function that likely has some injectable services/values...
配置功能的注入器( config:(uiRouter:UIRouter,注入器:Injector,模块:StatesModule)
)只能用于获取服务实例,但是不能调用带有可注入参数的函数。
Configuration function's injector (config: (uiRouter: UIRouter, injector: Injector, module: StatesModule)
) can only be used to get service instances, but can't invoke functions with injectable parameters.
在AngularJS中,这可以通过 $ injector.invoke(...)完成。
会调用该函数并注入其参数。
In AngularJS, this would be accomplished by $injector.invoke(...)
which would call the function and inject its parameters.
当 default
被定义为具有可注入函数时,应该如何处理。
How should I handle default
when it's defined as a function with injectables.
推荐答案
在Angular中,没有与AngularJS $ injector.invoke
直接对应的对象,因为可注入函数应该是 useFactory
提供者
There is no direct counterpart to AngularJS $injector.invoke
in Angular, because injectable functions are expected to be useFactory
providers that are defined on design time.
在AngularJS中只有一个注入器实例,但是在Angular中只有一个注入器层次结构,这也使事情变得复杂,因为应该对注入器存在依赖性
There is only one injector instance in AngularJS but a hierarchy of injectors in Angular, this also complicates things because a dependency is supposed to exist on injector that invokes a function.
处理此问题的惯用方法是定义所有希望作为提供者调用的函数。这意味着一个函数只能使用在(根或子模块)定义的注入器上的实例:
The idiomatic way to handle this this is to define all functions that are expected to be invoked as providers. This means that a function is restricted to use instances from injector it was defined on (root or child module):
export function fooDefaultStateFactory(baz) {
return () => baz.getStateName();
}
@NgModule({
providers: [
Baz,
{
provider: fooDefaultStateFactory,
useFactory: fooDefaultStateFactory,
deps: [Baz]
}
],
...
})
...
// relative state name
default: '.child',
...
// function with DI injectables
default: fooDefaultStateFactory
然后可以像其他依赖项一样从注入器中检索工厂函数并调用:
Then factory functions can be retrieved as any other dependencies from injector and called:
transition => {
...
if (typeof to.default === 'string') {
...
} else if (to.default) {
const defaultState = injector.get(to.default);
if (typeof defaultState === 'function') {
// possibly returns a promise
Promise.resolve(defaultState()).then(...)
} else { ... }
}
}
A与任何功能配合使用的 $ injector.invoke
对应物应大致类似于Angular 2/4中构造函数定义的工作方式 Class
helper (在Angular 5中已弃用)。区别在于 Class
接受用数组或 parameters
静态属性注释的构造函数,注释应成为数组的数组,因为依赖项可能涉及修饰符(注入
,可选
等)。
A counterpart to $injector.invoke
that works with any function should loosely resemble how constructor definition works in Angular 2/4 Class
helper (deprecated in Angular 5). The difference is that Class
accepts constructor function that is annotated either with array or parameters
static property, the annotations are expected to be an array of arrays because dependencies can involve decorators (Inject
, Optional
, etc).
由于装饰器不适用于未注册为提供程序的函数,因此该数组应为纯数组,类似于 AngularJS隐式注释或 deps
在Angular <$ c $中c> useFactory 提供程序:
Since decorators aren't applicable to a function that wasn't registered as provider, the array is expected to be plain, similarly to AngularJS implicit annotations or deps
in Angular useFactory
provider:
function invoke(injector, fnOrArr) {
if (Array.isArray(fnOrArr)) {
const annotations = [...fnOrArr];
const fn = annotations.pop();
const deps = annotations.map(annotation => injector.get(annotation));
return fn(...deps);
} else {
return fnOrArr();
}
}
可以绑定到注射器实例:
Can be bound to injector instance:
const injectorInvoke = invoke.bind(injector);
injectorInvoke([Foo, Bar, (foo: Foo, bar: Bar) => {...}]);
并将调用函数的代码段修改为:
And the snippet that invokes a function is modified to:
...
if (typeof defaultState === 'function' || Array.isArray(defaultState)) {
// possibly returns a promise
Promise.resolve(injectorInvoke(defaultState)).then(...)
} else { ... }
...
这篇关于在Angular中调用具有依赖项的函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!