I am working on an Angular library.
Currently, when I want to retrieve data from my API I use my service:
@Injectable()
export class ProductService {
public getProduct(id: number): Observable<Product> {
// return response of http request
}
}
It is a basic method which only return a Product which is an interface Now, I improve my method to return a Product class which can contains methods, other parameters, etc.
@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;
})
)
}
}
Now Product is an instance of the Product class and I can implement methods in it like this:
export class Product extends Unserializable {
...
get variantsCount(): number {
return this.variants.length
}
...
}
At this point, everything is pretty clean and work well. But let's say I want to retrieve Product information that must be gathered from the API or add static functions which retrieve one or more Products:
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
...
})
)
}
...
}
It is a pattern I use a lot when working with VueJS. It would be possible to work with Products in a component like this:
ngOnInit() {
this.$product = Product.get(this.id);
this.$variants = this.$product.pipe(switchMap(product => product.variants))
this.$products = Product.list(5, 0)
}
After all theses lines of code, here is my question:
The class Product is outside of the Angular scope, it is neither a service nor a module. So I am not able to use dependency injection to get the ProductService (or any service like HttpClient). How can I achieve that ? Do I have to provide the service every time I instantiate a new Product ? Can I use a singleton service and retrieve it within my Product instance ?
I've found this question: Getting instance of service without constructor injection with a question which explain how to import a service everywhere in the application. Is there a better solution ? Or maybe my pattern is an anti-pattern with angular.