Reputation: 177
I was working on a project and came across a service which does not have the @Injectable() decorator, and works fine. Till now , I was under the impression that in angular if we want to implement DI we must use the @injectable() decorator and configure it with the provider. Configuring the service with a provider is mandatory, but it seems using @injectable() decorator is not. It is being used only in some services. The only difference I have noticed in the services using the decorator and the ones that don't is that the former have some dependencies themselves, while the latter don't
I have two types of services:
Type1:
export class SharedService {
//do something
}
Type2:
@Injectable()
export class MainCreditService {
constructor(private http: Http, private config: Config) {
this.config.EndPoints.subscribe(endpointObj => {
this.environment = endpointObj;
});
}
app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { FormsModule, ReactiveFormsModule} from '@angular/forms';
import { HttpClientModule} from '@angular/common/http'
@NgModule({
declarations: [
AppComponent,
],
imports: [
BrowserModule,
HttpClientModule,
FormsModule,
ReactiveFormsModule,
AppRoutingModule,
],
exports: [],
providers: [MainService,SharedService],
bootstrap: [AppComponent]
})
export class AppModule { }
app.component.ts
import { Component } from '@angular/core';
import { HttpClient } from "@angular/common/http";
import { MainService } from '../../services/main.service';
import { SharedService } from '../../services/shared.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
constructor(private mainservice: MainService, private sharedService:
SharedService) {
//doSomething
}
Why does one need @Injectable() and the other doesn't
I have watched a few videos on DI and gone through some articles, but still having a hard time understanding that where should we use this decorator and why. Can someone please explain the purpose of this decorator or maybe paste links to sources that have explained it well.
Upvotes: 6
Views: 8439
Reputation: 21
Injectable() decorator is used to inject other services or objects into your service.
If your service do not have any dependencies then you don't need to add Injectable() decorator. But if your services has some dependencies then you must add Injectable() decorator else angular will not able to inject dependencies in your service and it will throw runtime error.
You have two services one with Injectable() decorator and other without Injectable().
You will notice that the MainCreditService has Injectable() decorator and also it has some dependencies in constructor i.e private http: Http, private config: Config. But SharedService does not have Injectable() decorator also it does not have dependencies too.
export class SharedService {
//do something
}
@Injectable()
export class MainCreditService {
constructor(private http: Http, private config: Config) {
this.config.EndPoints.subscribe(endpointObj => {
this.environment = endpointObj;
});
Upvotes: 2
Reputation: 2911
Yes, you are right about that we can create a service without Injectable() decorator. Then why we need this?
Injectable decorator or any other angular/custom decorator generates metadata. A special kind of metadata(design:paramtypes) is required to inject a service.
Without dependencies and without Injectable:
@Component({
selector: 'ponyracer-app',
template: '<h1>PonyRacer</h1>'
})
export class PonyRacerAppComponent {
constructor(private appService: AppService) {
console.log(appService);
}
}
// without Injectable
export class AppService {
constructor() {
console.log('new app service');
}
}
Here is javascript generated code for component and service. You can see some difference between component generated code and service generated code.
Angular added some of the metadata here as we used component decorator. One metadata is about appService. But service code has no metadata.
var AppService = (function () {
function AppService() {
console.log('new app service');
}
return AppService;
}());
exports.AppService = AppService;
var PonyRacerAppComponent = (function() {
function PonyRacerAppComponent(appService) {
this.appService = appService;
console.log(appService);
}
PonyRacerAppComponent = __decorate([
core_1.Component({
selector: 'ponyracer-app',
template: '<h1>PonyRacer</h1>'
}),
__metadata('design:paramtypes', [app_service_1.AppService])
], PonyRacerAppComponent);
return PonyRacerAppComponent;
}());
So what will happen if our service is dependent on some other service like HttpService as below?
export class AppService {
constructor(http: HttpService) {
console.log(http);
}
}
Here is generated code for service:
var AppService = (function() {
function AppService(http) {
console.log(http);
}
return AppService;
}());
exports.AppService = AppService;
Angular is not providing information about http variable and when we will run this code it will give an error like Error: Can't resolve all parameters for AppService: (?)..
Now, if this service is decorated with Injectable() or even if any custom empty decorator, it will create reference for http service metadata.
var AppService = (function() {
function AppService(http) {
console.log(http);
}
AppService = __decorate([
core_1.Injectable(),
__metadata('design:paramtypes', [http_service_1.HttpService])
], AppService);
return AppService;
}());
exports.AppService = AppService;
Upvotes: 12
Reputation: 2797
From the documentation: https://angular.io/guide/dependency-injection
You can configure injectors with providers at different levels of your app, by setting a metadata value in one of three places:
In the @Injectable() decorator for the service itself.
In the @NgModule() decorator for an NgModule.
In the @Component() decorator for a component.
The @Injectable() decorator has the providedIn metadata option, where you can specify the provider of the decorated service class with the root injector, or with the injector for a specific NgModule.
To utilize a class with DI it must have a provider, and the @Injectable() decorator is one way of registering a provider.
If you use @Injectable()
you will not have to explicitly add MyServiceClass to the providers property of your module or component.
In Summary @Injectable() is not mandatory because it is just another way of defining a provider. Likewise, if you add @Injectable({providedIn: 'root'})
to your service, you will not need to add the service to any providers
property on any module or component.
Suppose you have a component:
@Component({
selector: "service-tester",
templateUrl: "./service-tester.component.html",
styleUrls: ["./service-tester.component.css"],
providers: [ProvidedByComponentService]
})
export class ServiceTesterComponent {
constructor(
public componentService: ProvidedByComponentService,
public rootService: ProvidedByRootService
) {}
}
It injects two services: One at the root level and another provided in the component.
If we look at our services they do the same thing:
export class ProvidedByComponentService {
public clickCount: BehaviorSubject<number>;
constructor() {
this.clickCount = new BehaviorSubject<number>(0);
}
increment() {
this.clickCount.next(this.clickCount.value + 1);
}
}
the only difference is the service provided in the root has a decorator:
@Injectable({ providedIn: "root" })
export class ProvidedByRootService {
// same as previous service, code omitted for brevity.
}
Now the only difference between these services is how many instances will be created by the providers when a new <service-tester>
component is added to a template.
This means that when count is incremented in ProvidedByRootService
it will be incremented in every ServiceTesterComponent
, while count in ProvidedByComponentService
will only be incremented locally within that component
This allows you to declare providers in a flexible manner depending on whether a service can be a singleton or not.
Services, such as API services are good examples of services which may benefit from being provided in root, as they typically do not hold any state, or hold state that is relevant for the entire application.
Stackblitz of the example below:
Upvotes: 4
Reputation: 142
If you decorate a service with @Injectable()
decorator by using the provideIn
set to root
value, It will create singleton instance through out the application and uses the same instance for all its components whereever you have injected.
Eg:
class HomeComponent {
constructor(private userService: UserService) {}
}
Please refer the link: https://angular.io/guide/dependency-injection#injector-hierarchy-and-service-instances
Upvotes: 1