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

查看:90
本文介绍了在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屋!

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