Взаимодействовать с услугами Angular за пределами Angular - PullRequest
2 голосов
/ 03 августа 2020

Я работаю над библиотекой Angular.

В настоящее время, когда я хочу получить данные из моего API, я использую свой сервис:

@Injectable()
export class ProductService {

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

}

Это базовый c метод, который возвращает только Product, который является интерфейсом Теперь я улучшаю свой метод, возвращая класс продукта , который может содержать методы, другие параметры и т. д. c.

@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, или добавить stati c функции, извлекающие один или несколько продуктов:

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. Можно было бы работать с продуктами в таком компоненте:

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

После всех этих строк кода, вот мой вопрос:

Класс Product находится за пределами Angular scope, это ни сервис, ни модуль. Поэтому я не могу использовать внедрение зависимостей для получения ProductService (или любой другой службы, например HttpClient). Как я могу этого добиться? Должен ли я предоставлять услугу каждый раз, когда я создаю новый продукт? Могу ли я использовать одноэлементную службу и получить ее в своем экземпляре продукта?

Я нашел этот вопрос: Получение экземпляра службы без внедрения конструктора с вопросом, объясняющим, как импортировать сервис повсюду в приложении. Есть ли лучшее решение ? Или, может быть, мой шаблон - это анти-шаблон с angular.

Ответы [ 2 ]

0 голосов
/ 03 августа 2020

Посмотрев на разные ответы и комментарии. Похоже, что это антипаттерн для Angular.

Где бизнес-логи c должны управляться службами и / или компонентами.

Как я все еще хочу иметь отвечая на мой вопрос, я провел небольшое исследование и нашел решение, соответствующее моим потребностям.

Прежде всего, я искал, как внедрить службу без автоматического c инжектора зависимостей, поскольку я не в Служба или компонент и нашел этот поток: Получение экземпляра службы без внедрения конструктора

Я хотел создать свою собственную инъекцию зависимостей, чтобы иметь чистый способ инъекции моих служб вместо выполнения 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();
  }

}

И здесь у нас есть (почти) хорошая инъекция зависимости в классе, который не является ни компонентом, ни службой!

0 голосов
/ 03 августа 2020

Относительно этой части:

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

Я бы сказал, что класс Product больше не имеет смысла как способ создания экземпляра продукта, если вы просто используете методы c stati.

В основном ваш класс Product ведет себя как служба, и вам следует подумать о том, чтобы изменить его на службу (что позволит вам использовать DI).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...