2

I'm trying to use Angular2 dependency injection but get the following error message:

error NG2003: No suitable injection token for parameter 'service' of class 'PaymentService'

app.module.ts - provider with factory method

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule
  ],
  providers: [
    PaypalPayment,
    CardPayment,
    {
      provide: PaymentService,
      useFactory: () => {
        return new PaymentService(new PaypalPayment());
      }
    }
  ],
  bootstrap: [AppComponent]
})
export class AppModule {
}

payment.service.ts - injectable payment service

@Injectable()
export class PaymentService {

  constructor(private service: Payment) {
  }

  pay(amount: number) {
    console.log('Payment Service -> pay(...) is running');
    this.service.pay(amount);
  }
}

payment.interface.ts, card-payment.ts, paypal-payment.ts files

export interface Payment {
  pay(amount: number);
}

@Injectable()
export class CardPayment implements Payment {
  pay(amount: number) {
    console.log('Paid by card. Amount=', amount);
  }
}

@Injectable()
export class PaypalPayment implements Payment {
  pay(amount: number) {
    console.log('Paid via Paypal. Amount=', amount);
  }
}

Note: Everything works fine if I replace the interface "Payment" in PaymentService file with one of its implementations (PaymalPayment or CardPayment). But it's a pretty common case to have an interface there.

The full source code is here

  1. git clone https://github.com/karenmargaryan/di-via-interfaces-issue
  2. cd di-via-interfaces-issue
  3. npm install
  4. ng serve

1 Answers1

2

In payment.service.ts :

Update the Constructor

//import { Inject } from '@angular/core';

constructor(@Inject(Payment) private service: Payment)
Useme Alehosaini
  • 2,998
  • 6
  • 18
  • 26
  • Your version is working. Thank you very much for your reply. But do you understand what is the problem with my version? I even found an example like I wrote in Ari Lerner's book, but his version also doesn't work. – Karen Margaryan Mar 29 '20 at 00:43
  • Sure, you're welcome. In order to inject service in a constructor, the service type should be decorated as @Injectable() and this is usually done when you use "ng g s" CLI command. In your case you are trying to inject the service of type Payment while Payment is not decorated as @Injectable() so you should do manual injection by @Inject() . If you found the answer beneficial, do not forget to mark the answer as accepted :) – Useme Alehosaini Mar 29 '20 at 01:36
  • I agree with you about decorator, but there is an exception here. If I don't create an instance of "Payment" (it's an interface), and I inject something there, then why shall I decorate with "Injectable()". If you just replace the interface with base class (without decorator), it will work perfectly. And as I understand that is against your answer. Am I right? So the main secret here is why this works with "class Payment" and doesn't work with "interface Payment"... Anyway thank you for your effort and explaination . – Karen Margaryan Mar 29 '20 at 17:35
  • @Inject decorator is only needed for injecting primitives. The primitive types are number, string, boolean, bigint, symbol, null, undefined, or object. In your case, the interface is considered primitive so it required this decorator. – Useme Alehosaini Nov 16 '20 at 16:56
  • I skipped this answer at first because I thought there was no way this would be a reasonable fix, but it worked perfectly for me. Thanks! – Ruzihm Aug 05 '22 at 22:11