Reputation: 295
I am using an abstract class to show some data from different sources. Every source is injected in the abstract class to show the data in a component.
This is my Component, which is using the abstract class to get the data:
import {AbstractclassService} from '../../../../abstractclass.service';
import {Source2-Service} from '../../../../Source2.service';
import {Source1-Service} from '../../../../Source1.service';
@Component({
selector: 'app-gauge',
templateUrl: './gauge.component.html',
providers: [
{
provide: AbstractclassService,
useValue: Source1-Service , Source2-Service
multi: true
}
],
styleUrls: ['./gauge.component.css']
})
export class GaugeComponent implements OnInit {
data = [
{
name: 'test',
value: 'test'
}
];
constructor( public abstractclassService: AbstractclassService ) {}
ngOnInit () {
this.abstractclassService.onMessage = (msg: string) => {
this.data = [{name: 'test', value: msg}];
};
}
And this is my abstract-class as service:
@Injectable()
export abstract class AbstractclassService {
public onMessage(msg: string): void {
console.log("Testing");
}
}
Now, i didn't get it how to inject in useValue the different sources?
Upvotes: 1
Views: 3351
Reputation: 2531
For a similar problematic, I solve it by having an interface and a list of InjectionToken. That's a bit overkill, but that's allow a lot of flexibility and can by apply to other issues.
After setup a default search service in a shared module, multiple custom implementation can be provided (component/module/service). And that without having to explicitly inject (so reference) all possible implementation.
Interface and default implementation
export interface ISearch {
searchByTerm(textInput: string);
searchByObject(objInput: Object);
}
export class DefaultSearch implements ISearch {
searchByTerm(textInput: string) { console.log("default search by term"); }
searchByObject(objInput: Object) { console.log("default search by object"); }
}
Create list of service implementation with InjectionToken
// Keep list of token, provider will give a implementation for each of them
export const SearchServiceTokens: Map<string, InjectionToken<ISearch>> = new Map();
// Add File service implementation token
SearchServiceTokens.set('default', new InjectionToken<ISearch>('default'));
Provider for default service implementation
providers: [
...
// Default implementation service
{
provide: SearchServiceTokens.get('default'),
useClass: DefaultSearch
}
]
Custom implementation (could be on another module)
export class Component1Search implements ISearch {
searchByTerm(textInput: string) { console.log("component1 search by term"); }
searchByObject(objInput: Object) { console.log("component1 search by object"); }
}
Add token for custom implementation
SearchServiceTokens.set('component1', new InjectionToken<ISearch>('component1'));
Add provider
providers: [
...
// Other implementation service
{
provide: SearchServiceTokens.get('component1'),
useClass: Component1Search
}
]
Finally, in a component, global service, ...
searchService: ISearch;
constructor(private injector: Injector) {
// Use default
this.searchService = this.injector.get(SearchServiceTokens.get('default'));
}
setCustomServiceServiceByToken(customSearchServiceToken) {
// Use custom service
this.searchService = this.injector.get(SearchServiceTokens.get(customSearchServiceToken));
}
Upvotes: 1
Reputation: 1513
Use value in provider is not the good way for your use case.
Source1-Service and Source2-Service must be classes which extends the abstract class. After that you inject into two distinct providers your services.
When a class extends an abstract class, the extended classes have to define the method onMessage
(here).
So, your component can be look like:
import { Component, Inject } from '@angular/core';
import { Observable } from "rxjs"
abstract class AbstractClassService{
abstract onMessage(msg: string): {name: string, value: string}
}
class Source1-Service extends AbstractClassService{
onMessage(msg: string) {
return {name: "test", value: msg}
}
}
class Source2-Service extends AbstractClassService{
onMessage(msg: string) {
return {name: "test2", value: msg}
}
}
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ],
providers: [
{provide: "Impl1", useClass: Source1-Service},
{provide: "Impl2", useClass: Source2-Service}
]
})
export class AppComponent {
msg1: {name: string,value:string}[]
msg2: {name: string,value:string}[]
constructor(@Inject("Impl1") private service1:AbstractClassService,
@Inject("Impl2") private service2:AbstractClassService) {
this.msg1 = [this.service1.onMessage("msg1")]
this.msg2 = [this.service2.onMessage("msg2")]
}
}
For the full code: https://stackblitz.com/edit/angular-e8sbg8?file=app%2Fapp.component.ts
For futher
I think it's not a good idea to use the abstract class in this case. You should prefer to use an interface
And, I invite to you to read the DI angular documentation here: https://angular.io/guide/dependency-injection#providers to have more information
Upvotes: 2