Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inject an Interface with Angular 4

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?

like image 750
lkartono Avatar asked Mar 04 '26 07:03

lkartono


1 Answers

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.

like image 123
Frank Modica Avatar answered Mar 06 '26 23:03

Frank Modica



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!