问题
I'm trying to create a generic DeleteableConfirmationComponent
that will allow me to show a confirmation dialog and invoke the delete
method from any injected service implementing a Deleteable
infterface.
To do so, I've created this Interface:
export interface Deleteable {
delete(object);
}
and I have a service that implements it:
@Injectable()
export class LocalityService implements Deleteable {
delete(locality): Observable<Locality> {
// Delete logic.
}
}
For the DeleteableConfirmationComponent
, I've tried to inject the service using constructor:
export class DeleteableConfirmationComponent {
constructor(
public dialog: MdDialogRef<DeleteableConfirmationComponent>,
@Inject(MD_DIALOG_DATA) public data: any,
private service: Deleteable
) {}
delete() {
this.service.delete(this.object)
.subscribe(() => {
this.dialog.close();
});
}
}
but unfortunately, I've got an error saying it can't resolve all parameters for DeleteableConfirmationComponent.
For now, I'm using the dialog data options, in order to pass my service:
confirmDelete(locality) {
this.dialog.open(DeleteableConfirmationComponent, {
data: {
service: this.localityService
}
});
}
but it feels dirty and does allow any kind of service to be injected while I want to force service that implement the Deleteable
interface.
I was thinking I could probably be better going with an abstract class
but I'm more a fan of composition over inheritance.
Any idea or best practice advise?
回答1:
As mentioned in the comments, you can convert your interface to an abstract class:
export abstract class Deleteable {
abstract delete(object);
}
Then in your providers you can map it to the real class:
providers: [{ provide: Deleteable, useValue: new LocalityService() }]
You may not like this approach, because it seems like now LocalityService
must extend Deleteable
. But what if LocalityService
needs to extend some other class? Multiple inheritance is not allowed:
// Error: Classes can only extend a single class
export class LocalityService extends OtherClass, Deleteable { }
Or you may simply not like the fact that Deleteable
will now show up in the prototype chain of LocalityService
:
export class LocalityService extends Deleteable {
delete(locality): void {
// Returns true
alert(this instanceof Deleteable);
}
}
However, as shown in this answer, TypeScript allows you to treat a class like an interface. So you can use implements
with an abstract class.
export class LocalityService extends OtherClass implements Deleteable {
delete(locality): void {
// Returns false
alert(this instanceof Deleteable);
}
}
So for all intents and purposes, your abstract class is now behaving like an interface. It won't even show up in the prototype chain.
回答2:
It is possible with InjectionToken
a replacement for the deprecated OpaqueToken
export const AuthenticationProvider = new InjectionToken(
"AuthenticationProvider",
{ providedIn: "root", factory: () => new CognitoAuthenticationProvider() }
);
...
@Injectable()
export class CognitoAuthenticationProvider implements IAuthenticationProvider {
...
@Injectable({
providedIn: "root"
})
export class AuthenticationService {
constructor(
@Inject(AuthenticationProvider)
private authenticationProvider: IAuthenticationProvider,
private http: HttpClient
) {}
来源:https://stackoverflow.com/questions/45117934/inject-an-interface-with-angular-4