在Angular

时间:2018-01-22 18:45:35

标签: angular typescript configuration routing angular5

使用Angular 5和UIRouter状态路由。我根据此界面使用了其他自定义路由状态属性。

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

当我定义一个抽象状态时,我现在也可以为它添加一个default属性,所以当一个人试图路由到一个抽象状态时,默认应该将它们重定向到配置的 default < / em>儿童状态。

从上面的界面可以理解,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) ......

配置函数的注入器($injector.invoke(...))只能用于获取服务实例,但不能使用可注入参数调用函数。

在AngularJS中,这将由default完成,它将调用该函数并注入其参数。

主要问题

如果将$("table tr:not('.jack')").filter(":odd").children().css("background-color", "red");定义为具有注射剂的功能,我该如何处理。

2 个答案:

答案 0 :(得分:1)

Angular中与AngularJS $injector.invoke没有直接对应关系,因为可注入函数应该是在设计时定义的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中构造函数定义的工作原理大致相似(在Angular 5中已弃用)。区别在于Class接受使用数组或parameters静态属性注释的构造函数,注释应该是一个数组数组,因为依赖项可能涉及装饰器(Inject,{ {1}}等)。

由于装饰器不适用于未注册为提供者的函数,因此该数组应该是纯文本的,类似于Angular中的AngularJS implicit annotationsOptional {{1}提供者:

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

答案 1 :(得分:0)

这是您可以通过添加有关解析服务的部分来解决此问题的一种方法。

// THE IMPORTANT PART
config: (uiRouter: UIRouter, injector: Injector, module: StatesModule) => {
   uiRouter.transitionService.onBefore(
      // HookMatchCriteria
      {
         to: state => state.abstract === true && !!state.self.default
      },
      // TransitionHookFn
      transition => {
         let to: transition.to();
         if (typeof to.default === "string") {
            return transition.router.stateService.target(to.default);
         } else if (typeof to.default === "function") {
            let functionPromise = Promise.resolve(to.default(injector));
            return functionPromise.then((toDefault) => transition.router.stateService.target(toDefault));
         }
}

检查默认值是否为string的第一部分是显而易见的。 在第二个if中,我检查参数是否为函数,这会自动将其视为TypeScript中的函数,因此可以直接调用它。在结果上可以是Promise<string>string i,然后使用Promise.resolve(value)方法。此方法为两个输入返回一个新的Promise<string>,然后我们可以通过调用stateService来链接它,它将返回Promise<TargetState>,这是TransitionHookFn的有效返回。

要解析服务,您可以更改界面,如下所示:

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

然后你也会在调用函数时始终发送注入器,因为你在config方法中有它。如果预期所有参数都是服务,则删除...args并发送注入器。

然后,您作为样本提供的函数将如下所示:

// relative state name
default: '.child',
// absolute state name
default: 'parent.child',
// function with DI injectables
default: (injector: Injector) => {
    let auth: AuthService = injector.get(AuthService);
    let stateService: StateService = injector.get(StateService);
    if (auth.isAuthenticated) {
        return '.child';
    } else {
        return stateService.target('.login', { ... });
    }
}
// function with DI injectables returning a promise
default: (injector: Injector) => {
    let items: ItemsService = injector.get(ItemsService);
    return items
        .getTotal()
        .then((count) => {
            return count > 7
                ? '.simple'
                : '.paged';
        });