【问题标题】:Abstracting @Injectable with Angular Ivy does not work使用 Angular Ivy 抽象 @Injectable 不起作用
【发布时间】:2020-04-27 12:06:51
【问题描述】:

更新

简介

在 Angular 中使用装饰器 @Injectable 提供服务。

@Injectable() // -> works
export class MyService {
  constructor() {}
}

抽象@Injectable

在 Ivy 之前,可以为 @Injectable 构建抽象(例如,用于动态配置提供程序、增强服务类)。

以下 sn-p 显示了如何包装 @Injectable 的示例。

function InjectableEnhanced() {
  return <T extends new (...args: any[]) => InstanceType<T>>(target: T) => {
    Injectable({ providedIn: "root" })(target);
  };
}

在启用 Ivy 时,使用装饰器 InjectableEnhanced(见上文)不起作用。 以下代码被截断会导致运行时错误。

@InjectableEnhanced() // -> does not work
export class MyService {
  constructor() {}
}

运行时错误

使用 @InjectableEnhanced 和 angular/cli 编译服务有效,但浏览器中显示以下错误。对应的项目可以在https://github.com/GregOnNet/ng-9-inject.git找到。

也许,Angular 编译器做了一些代码转换,但不能再在其他装饰器中解析 @Injectable。 查看 Angular 存储库,可以在 injectable.ts 中找到对 JIT 编译器的引用(请参阅:https://github.com/angular/angular/blob/master/packages/core/src/di/injectable.ts#L14)。

问题

还有没有办法抽象出@Injectable?

复制库

https://github.com/GregOnNet/ng-9-inject.git

【问题讨论】:

    标签: angular typescript decorator angular-ivy


    【解决方案1】:

    可以使用一些 Angular 的内部 API 创建自定义提供程序:

    import { ɵɵdefineInjectable, ɵɵinject } from "@angular/core";
    
    export function InjectableEnhanced() {
      return <T extends new (...args: any[]) => InstanceType<T>>(target: T) => {
        (target as any).ɵfac = function() {
          throw new Error("cannot create directly");
        };
    
        (target as any).ɵprov = ɵɵdefineInjectable({
          token: target,
          providedIn: "root",
          factory() {
            // ɵɵinject can be used to get dependency being already registered
            const dependency = ɵɵinject(Dependency); 
            return new target(dependency);
          }
        });
        return target;
      };
    }
    

    可以在https://github.com/GregOnNet/ng-9-inject找到工作示例

    【讨论】:

    • 似乎在 Angular 12 中 ɵfac 只是一个吸气剂,不可能为它分配一个函数。任何想法如何克服这个问题?
    • 嗨,我最近更新了我的库,我的库仍然可以使用。但我会仔细检查。
    • 好的,我也验证过了。所以它与 AOT=true 一起工作。但是对于使用 Ivy Angular 的非 aot 构建,添加了您无法修改的 ɵfacɵprov getter。对于这种情况,创建普通的 Injectable 实例仍然像以前一样工作。我只想知道是否有办法知道构建是否在运行时...
    • 好的,如果其他人有同样的问题,这里是如何解决它。而不是(target as any).ɵprov = ɵɵdefineInjectable 使用Object.defineProperty(target, 'ɵprov', {.....})
    【解决方案2】:

    装饰器按预期附加到构造函数,但是当 AppComponent 创建时,注入器尝试解析提供程序并崩溃。

    我认为错误消息只是组件构造失败时的一般错误,但是当 Angular 尝试为 AppComponent 构造函数获取可注入时发生错误。

    如果您记录服务的构造函数,您可以看到提供者元数据已附加:

    @InjectableEnhanced()
    export class MyService {
      constructor() {
      }
    }
    
    
    console.log((MyService as object).prototype.constructor.hasOwnProperty('ɵprov'));
    // prints "true"
    

    当我尝试检查该属性时,它会触发错误:

    try {
      console.log((MyService as object).prototype.constructor.ɵprov);
    } catch (err) {
      console.log(err); // prints the same error message
    }
    

    我认为该属性是一个 getter 属性,它解析为提供者的实例,这就是崩溃的原因。

    我能找到的关于 Angular 的最接近的问题是这个,但它仍然是开放的:

    https://github.com/angular/angular/issues/31495

    所以我觉得 Ivy 编译器可能正在搜索 @Injectable() 的源代码并构建预期提供者的列表,但它没有看到这个新的装饰器,因此 MyService 被排除在列表之外。稍后在运行时,装饰器的元数据就在那里,但注入器不知道它的用途并崩溃。

    我试图找到一些文档,您可以在其中使用 Ivy 编译器注册一个新的装饰器,但没能做到,我不知道这样的事情是否存在。

    仅供参考:我在我的其他一个项目中做同样的事情,所以我认为会有很多人受此影响。

    【讨论】:

    • 谢谢您的回答。 ? 它真的帮助我更深入地挖掘。我读了github.com/angular/angular/issues/31495,也认为这可能是相关的。不过,我将打开另一个问题,因为 IoC 也在这里受到影响。
    • @GregorWoiwode 你能用你打开的问题的链接更新你的问题,以便其他人可以找到它。谢谢。
    猜你喜欢
    • 2017-04-12
    • 1970-01-01
    • 2017-01-08
    • 1970-01-01
    • 2019-01-19
    • 1970-01-01
    • 2018-01-05
    • 2019-01-04
    • 1970-01-01
    相关资源
    最近更新 更多