在 Angular 中调用具有依赖关系的函数

2024-04-05

使用 Angular 5 和 UIrouter 状态路由。我根据此接口使用附加的自定义路由状态属性。

interface AugmentedNg2RouteDefinition extends Ng2StateDeclaration {
    default?: string | ((...args: any[]) => string | Promise<string>);
}

当我定义一个抽象状态时,我现在可以添加一个default属性也是如此,因此当尝试路由到抽象状态时,默认情况下应将它们重定向到已配置的状态default儿童状态。

从上面的界面可以看出,default可以定义为以下任意一项:

// 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工作中,我必须配置路由转换服务:

@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当它是一个可能具有一些可注入服务/值的函数时......

配置函数的注入器(config: (uiRouter: UIRouter, injector: Injector, module: StatesModule)) 只能用于获取服务实例,但不能调用可注入参数的函数。

在 AngularJS 中,这可以通过以下方式完成$injector.invoke(...)它将调用该函数并注入其参数。

主要问题

我该如何处理default当它被定义为带有可注入的函数时。


没有与 AngularJS 直接对应的东西$injector.invoke在 Angular 中,因为可注入函数预计是useFactory在设计时定义的提供者。

AngularJS 中只有一个注入器实例,但 Angular 中有一个注入器层次结构,这也使事情变得复杂,因为调用函数的注入器应该存在依赖关系。

处理这个问题的惯用方法是定义所有期望作为提供者调用的函数。这意味着函数仅限于使用定义在其上的注入器(根或子模块)中的实例:

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

然后可以从注入器中检索工厂函数作为任何其他依赖项并调用:

  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 https://github.com/angular/angular/blob/4.4.6/packages/core/src/util/decorators.ts#L99-L142(在 Angular 5 中已弃用)。不同之处在于Class接受用数组或注释注释的构造函数parameters静态属性,注释应该是数组的数组,因为依赖项可能涉及装饰器(Inject, Optional, etc).

由于装饰器不适用于未注册为提供者的函数,因此该数组应该是普通的,类似于AngularJS 隐式注释 https://docs.angularjs.org/guide/di#implicit-annotation or deps在角度useFactory提供者:

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

可以绑定到注入器实例:

const injectorInvoke = invoke.bind(injector);
injectorInvoke([Foo, Bar, (foo: Foo, bar: Bar) => {...}]);

调用函数的代码片段修改为:

  ...
  if (typeof defaultState === 'function' || Array.isArray(defaultState)) {
    // possibly returns a promise
    Promise.resolve(injectorInvoke(defaultState)).then(...)
  } else { ... }
  ...
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 Angular 中调用具有依赖关系的函数 的相关文章

随机推荐