在 Angular 中调用具有依赖项的函数 [英] Invoking a function with dependencies in Angular

查看:23
本文介绍了在 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.

从上面的界面可以理解,default可以定义为以下任意一种:

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';
        });
}

要真正使 default 工作,我必须配置路由转换服务:

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 { }

所以问题是调用 default 时它是一个可能有一些可注入服务/值的函数......

So the problem is invoking the default when it's a function that likely has some injectable services/values...

配置函数的注入器(config: (uiRouter: UIRouter, injector: Injector, module: 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.

推荐答案

AngularJS $injector.invoke 在 Angular 中没有直接对应物,因为可注入函数应该是 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 { ... }
    }
  }

$injector.invoke 一起使用的任何函数的对应物应该大致类似于 Angular 2/4 中构造函数定义的工作方式 Class helper(在 Angular 5 中已弃用).不同之处在于 Class 接受带有数组或 parameters 静态属性注释的构造函数,注释应该是数组数组,因为依赖项可能涉及装饰器(InjectOptional 等).

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 useFactory provider:

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆