Reputation: 31
I've gone through several articles and official Angular guides briefly but it seems that they couldn't help me to solve my task. And here is what I wanted and did.
Let's say I have Angular application with product listing page. Moreover this app will have category listing page and some N listing pages in future. As you can see they're quite similar and will have one component in common - data table.
<app-data-table [type]="'product'"></app-data-table>
Which is implemented like:
import {Component, Input, OnInit} from '@angular/core';
import {DataFactoryService} from "../data-factory.service";
@Component({
selector: 'app-data-table',
templateUrl: './data-table.component.html',
styleUrls: ['./data-table.component.css']
})
export class DataTableComponent implements OnInit {
@Input() type: string;
private data: any[];
constructor(private dataFactory: DataFactoryService) { }
ngOnInit(): void {
this.dataFactory.getServiceBy(this.type).selectAll();
}
}
So as you might guess already I meant to make this component service type agnostic. That's why I've created and injected that DataFactory
:
import { Injectable } from '@angular/core';
import {ProductService} from "./product.service";
import {CategoryService} from "./category.service";
import {DataService} from "./data.service";
@Injectable({
providedIn: 'root'
})
export class DataFactoryService {
private serviceTokenMapping = {
"product": ProductService,
"category": CategoryService
};
constructor() { }
public getServiceBy(token: string): DataService {
return new this.serviceTokenMapping[token];
}
}
And at the end we have two services for products and categories with some simple basic abstract class:
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export abstract class DataService {
abstract selectAll(): any[];
}
import { Injectable } from '@angular/core';
import {DataService} from "./data.service";
import {Product} from "./product";
@Injectable({
providedIn: 'root'
})
export class ProductService implements DataService {
constructor() {}
public selectAll(): Product[] {
console.log(`ProductService...`);
return [];
}
}
import { Injectable } from '@angular/core';
import {DataService} from "./data.service";
import {Category} from "./category";
@Injectable({
providedIn: 'root'
})
export class CategoryService implements DataService {
constructor() {}
public selectAll(): Category[] {
console.log(`CategoryService...`);
return [];
}
}
The funny part here is that this implementation works exactly as a expected. So I'm passing type of table as product for product related page, category type is for category etc.
Question is did I do something incorrect from perspective of Angular style (providers, DI etc.) and do we have any way to implement such a requirement to be more Angular-ish?
Upvotes: 3
Views: 4636
Reputation: 54
I think you actually instantiating ProductService
and CategoryService
every time you trigger this.dataFactory.getServiceBy(this.type)
and not actually using the dependency injection in Angular.
You can use the providers
property of @NgModule
to specify token per dependency and fetch the dependency using @Inject
or injector.get
.
export interface DataService {
selectAll(): any[];
}
@Injectable({
providedIn: 'root'
})
export class ProductService implements DataService {
public static TOKEN = new InjectionToken<DataService>('ProductService_TOKEN');
constructor() {}
public selectAll(): Product[] {
console.log(`ProductService...`);
return [];
}
}
@Injectable({
providedIn: 'root'
})
export class CategoryService implements DataService {
public static TOKEN = new InjectionToken<DataService>('CategoryService_TOKEN');
constructor() {}
public selectAll(): Category[] {
console.log(`CategoryService...`);
return [];
}
}
@NgModule({
providers: [
{
provide: ProductService.TOKEN,
useExisting: forwardRef(() => ProductService),
multi: false
},
{
provide: CategoryService.TOKEN,
useExisting: forwardRef(() => CategoryService),
multi: false
}
]
})
export class YourModule {}
@Component({
selector: 'app-data-table',
templateUrl: './data-table.component.html',
styleUrls: ['./data-table.component.css']
})
export class DataTableComponent implements OnInit {
@Input() type: string;
private data: any[];
constructor(
@Inject(ProductService.TOKEN) private dataService: DataService,
private injector: Injector
) { }
ngOnInit(): void {
this.dataService.selectAll();
this.injector.get<DataService>(CategoryService.TOKEN).selectAll();
}
}
Upvotes: 1