4

更新

介绍

在 Angular 中,服务是使用装饰器提供的@Injectable

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

抽象@Injectable

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

以下片段显示了如何@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() {}
}

运行时错误

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

在此处输入图像描述

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

问题

还有一种抽象@Injectable的方法吗?

复制库

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

4

2 回答 2

4

可以使用一些 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找到

于 2020-01-15T20:53:31.803 回答
1

装饰器按预期附加到构造函数,但是在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 编译器注册一个新的装饰器,但没能,我不知道这样的东西是否存在。

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

于 2020-01-09T21:39:28.117 回答