【问题标题】:Interact with Angular services outside of the Angular scope与 Angular 范围之外的 Angular 服务交互
【发布时间】:2020-08-03 18:38:13
【问题描述】:

我正在开发一个 Angular 库。

目前,当我想从我的 API 中检索数据时,我会使用我的服务:

@Injectable()
export class ProductService {

  public getProduct(id: number): Observable<Product> {
     // return response of http request
  }

}

这是一个基本方法,只返回一个产品是一个接口 现在,我改进了我的方法以返回一个 Product 类,它可以包含方法、其他参数等。

@Injectable()
export class ProductService {

  public getProduct(id: number): Observable<Product> {
     return this.http.get(`http://myapi/products/${id}`).pipe(
        map(response => response.data),
        map(productData => {
          const product = new Product()
          product.unserialize(productData)
          return product;
        })
     )
  }

}

现在 Product 是 Product 类的实例,我可以像这样在其中实现方法:

export class Product extends Unserializable {
  ...
  get variantsCount(): number {
    return this.variants.length
  }
  ...
}

此时,一切都非常干净并且运行良好。 但是假设我想检索必须从 API 收集的产品信息,或者添加检索一个或多个产品的静态函数:

export class Product extends Unserializable {
  ...
  public get $variants (): Observable<ProductVariants> {
    return this.productService.getVariants(this);
  }

  public static get(id: number): Observable<this> {
    return this.productService.getProduct(id).pipe(
        map(productData => {
          const product = new Product()
          product.unserialize(productData)
          return product;
        })
     )
  }

  public static list(limit: number, skip = 0): Observable<this[]> {
     return this.productService.getProducts(limit, skip).pipe(
        map(productsData => {
          // Unserialize every products in the array
          ...
        })
     )
  }

  ...
}

这是我在使用 VueJS 时经常使用的一种模式。可以在这样的组件中使用 Products:

ngOnInit() {
  this.$product = Product.get(this.id);
  this.$variants = this.$product.pipe(switchMap(product => product.variants))
  this.$products = Product.list(5, 0)
}

在所有这些代码行之后,这是我的问题:

Product 类在 Angular 范围之外,它既不是服务也不是模块。 所以我无法使用依赖注入来获取 ProductService(或任何像 HttpClient 这样的服务)。 我怎样才能做到这一点?每次实例化一个新产品时我都必须提供服务吗?我可以使用单例服务并在我的 Product 实例中检索它吗?

我发现了这个问题:Getting instance of service without constructor injection 有一个问题解释了如何在应用程序的任何地方导入服务。有更好的解决方案吗?或者也许我的模式是带有角度的反模式。

【问题讨论】:

  • 恕我直言,这个想法是使用该服务,所以是的,我将其称为反模式。可以将服务注入普通类,但我认为这不是一种非常干净的方式。 this.$product = Product.get(this.id);this.$product = this.productService.get(this.id); 到底有什么区别?!
  • 这种模式的作用是只使用一个类作为 Entity 和 EntityManager 并且服务将只是一个 client 用于简单地返回 http 请求响应的 api。跨度>

标签: angular typescript


【解决方案1】:

关于这部分:

ngOnInit() {
  this.$product = Product.get(this.id);
  this.$variants = this.$product.pipe(switchMap(product => product.variants))
  this.$products = Product.list(5, 0)
}

如果您只是使用静态方法,我会说 Product 类作为实例化产品的方式不再有意义。

基本上,您的 Product 类的行为类似于服务,您应该考虑将其更改为服务(这将允许您使用 DI)。

【讨论】:

  • 所以它是 Angular 的反模式?检索 Product 的variants 怎么样?我是否必须使用this.productService.getVariants(product)。它不是很干净,因为我必须将产品放在 Observable 之外
  • 是的,因为您遇到了依赖注入。它可以单独工作(它与 Presenter 模式 indepth.dev/model-view-presenter-with-angular 一起使用),但在这种情况下则不行。
  • this.$product.pipe(switchMap(product =&gt; product.variants)) 更改为 this.productService.get(id).pipe(switchMap(product =&gt; product.variants))
【解决方案2】:

在查看了不同的答案和 cmets 之后。看起来它是 Angular 的反模式。

业务逻辑必须由服务和/或组件管理。

由于我仍然想知道我的问题的答案,所以我做了一些研究并找到了适合我需要的解决方案。

首先,我搜索了如何在没有自动依赖注入器的情况下注入服务,因为我不在服务或组件中,并找到了这个线程:Getting instance of service without constructor injection

我想创建自己的依赖注入,以便以一种干净的方式注入我的服务,而不是在我需要服务的任何地方都执行Injector.get(service)

由于我仍然需要使用构造函数来实例化我的类并最终使用参数,我不得不找到另一种方法来指示我需要哪些服务。

所以我制作了一个名为 InjectServices 的装饰器,它将覆盖构造函数

export function InjectServices(...services: any): any {
  return (constructor: any) => {
    return class extends constructor {
      constructor(...args: any[]) {
        super(...args);

        // The AppInjector cannot be imported before or the module is not instantiated
        const {AppInjector} = require('../mymodule.module');

        for (const service of services) {
          this[Utils.camelize(service.name)] = AppInjector.get(service);
        }
      }
    };
  };
}

它在输入中接受服务,重写构造函数以迭代参数并将服务注入到实例中以服务的骆驼化名称。

我必须在模块实例化时定义AppInjector

export let AppInjector: Injector;

@NgComponent({...})
export class MyModule {
  constructor(private injector: Injector) {
    AppInjector = injector;
  }
}

我现在可以在我的应用程序的任何地方使用装饰器

import { InjectServices } from '../decorators/inject-service';
import { ProductService } from '../services/product.service';

@InjectServices(ProductService)
export class ProductEntity {

  // This line is just for typing
  private productService: ProductService;

  test(): void {
    this.productService.test();
  }

}

在这里,我们在一个既不是组件也不是服务的类中有一个(几乎)很好的依赖注入!

【讨论】:

    猜你喜欢
    • 2016-03-29
    • 2020-12-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多