36

Problem Statment

I am learning Angular 4 and I have stumble upon a code where @Inject is being used in a constructor and I am not able to figure out why...

Code and Source

I am using Angular 4 Material

Code Source: https://material.angular.io/components/dialog/overview

In the code, they are injecting MAT_DIALOG_DATA

constructor(public dialogRef: MatDialogRef<DialogOverviewExampleDialog>,
             @Inject(MAT_DIALOG_DATA) public data: any
           ) { }

Can anyone please elaborate what does it mean and when/where should we do this?

Sunil Garg
  • 14,608
  • 25
  • 132
  • 189
Vikas Bansal
  • 10,662
  • 14
  • 58
  • 100

3 Answers3

10

@Inject() is a manual mechanism for letting Angular know that a parameter must be injected.

import { Component, Inject } from '@angular/core';
import { ChatWidget } from '../components/chat-widget';

@Component({
  selector: 'app-root',
  template: `Encryption: {{ encryption }}`
})
export class AppComponent {
  encryption = this.chatWidget.chatSocket.encryption;

  constructor(@Inject(ChatWidget) private chatWidget) { }
}

In the above we've asked for chatWidget to be the singleton Angular associates with the class symbol ChatWidget by calling @Inject(ChatWidget). It's important to note that we're using ChatWidget for its typings and as a reference to its singleton. We are not using ChatWidget to instantiate anything, Angular does that for us behind the scenes

From https://angular-2-training-book.rangle.io/handout/di/angular2/inject_and_injectable.html

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
Rahul Singh
  • 19,030
  • 11
  • 64
  • 86
  • 1
    Injecting `ChatWidget` component to make the `component` behave like a `singleton service` so that the component state remain same across the app. Is it the motive? Have I got the point? – Vikas Bansal Nov 01 '17 at 08:06
  • 1
    @VikasBansal Rahul is right but that is not the case here, even if `@Inject` is not used here and the service is provided in a root level, the service is still a singleton. If you read the paragraph just below the link in this answer you will see that it makes no difference. – eko Nov 01 '17 at 08:08
  • 1
    not the component , but to make use of the `singleton service` in the component . – Rahul Singh Nov 01 '17 at 08:08
  • Ok but the services are already singleton through the hierarchy. e.g. If I inject a service in `ModA` the same instance of the service will be used through `ModA` children Modules... won't it? Also `ChatWidget` is a `Service` not a `Component`... correct? I am sorry for the trouble but I am facing hard-time to understand all these concepts. – Vikas Bansal Nov 01 '17 at 08:12
  • if you donot provide the service at the root level it is not a singleton . you can make use of services just for one component also – Rahul Singh Nov 01 '17 at 08:12
  • so when we need to inject a `service` for a single `component` or some `service` we use `@inejct`. One more thing... Will the service behave as `singleton` across the app or only for the sub-components? – Vikas Bansal Nov 01 '17 at 08:16
  • 1
    it will behave a singleton across the app if placed at root level else only on component level – Rahul Singh Nov 01 '17 at 08:18
  • How about child components @RahulSingh ? – marinvirdol Nov 01 '17 at 10:06
  • 1
    I don't think it will trickle down to the child component. It will be singleton only for the component – Rahul Singh Nov 01 '17 at 10:07
  • Hi Rahul and Vikas, Aboce explanations were verful, Just one more query that if i inject any service to any component (not the root and which has some childs), will it not be singleton to its childs, Do i needs @Inject decorator to make it singleton? – amit wadhwani Apr 10 '18 at 09:56
  • By root here we mean the app component , if it is the app component then it will be singleton to the child else it will not. If it just a children routes to a component i dnt think it will – Rahul Singh Apr 10 '18 at 10:03
  • This looks like a pretty good explanation here too: https://ultimatecourses.com/blog/angular-dependency-injection – Arjun Kalidas Jan 06 '21 at 21:19
9

If MAT_DIALOG_DATA is a non-factory/class dependency (like string for your configs), you usually use @Inject.

Also check InjectionToken: https://angular.io/guide/dependency-injection#injectiontoken

One solution to choosing a provider token for non-class dependencies is to define and use an InjectionToken


Here's a plunker: http://plnkr.co/edit/GAsVdGfeRpASiBEy66Pu?p=preview

if you remove @Inject in these cases you will receive a

Can't resolve all parameters for ComponentName: (?)

eko
  • 39,722
  • 10
  • 72
  • 98
  • Thank you for your answer, the example plunker was very helpful! I was wondering why would we want to use an `InjectionToken` vs maybe a simple constant that we can require from another shared file? Or maybe even just a variable defined on the component? – philip yoo Jul 26 '19 at 14:57
  • 1
    @philipyoo 1 reason might be that you might want to replace the services that uses the injection token. Another is the ModuleWithProviders approach; you can pass config option with the .forRoot() and use them in your classes via Inject – eko Jul 31 '19 at 09:05
9

IoC container in Angular uses the type declarations in the constructor to determine the objects to be injected to the constructor parameters.

In your example, "public data: any" parameter could not be determined by its type declaration because it's defined as "any". In order to solve this problem, you have to use "@Inject(MAT_DIALOG_DATA)" decorator to inform the IoC container about the object that must be injected to "data" parameter.

Also in your example, "@Inject" decorator is used with an InjectionToken to complicate things a little more :)

An InjectionToken is actually a class which is used to name the objects to be used by IoC container to inject in to other classes. Normally you could use any classes name as a token for IoC injection (like "MatDialogRef<DialogOverviewExampleDialog>" in your example) and this works fine. But when you start writing your UnitTests you realize that you need to use Mock objects instead of real objects to be injected into your classes and when you use real class names as your tokens, you could not do that.

To solve this problem you could use Interfaces as token names and this is actually the right solution, but since JavaScript does not support interfaces you could not use Interface names as tokens, because transpiled code does not contain Interface definitions.

As a result of all this, you need to use InjectionToken. An InjectionToken allows you to inject any object into your constructor. You just need to declare it in your modules and map to the real class that you want to be injected. This way you could use different classes for your production and test codes.

Yildiray Meric
  • 660
  • 6
  • 6