8315. Angular - Dependency InjectionDependency Injection
Learn Dependency Injection and how to use it in Angular application.
1. Dependency Injection
In software engineering, dependency injection
is a software design pattern that implements inversion of control for resolving dependencies. A dependency
is an object that can be used (a service). An injection
is the passing of a dependency to a dependent object (a client) that would use it.
Some of the advantages of dependency injection include the following:
- Your code is cleaner and more readable.
- Objects are loosely coupled.
- Possible to eliminate, or at least reduce, a component’s unnecessary dependencies.
- Reducing a component’s dependencies typically makes it easier to reuse in a different context.
- Increases a component’s testability.
- Moves the dependencies to the interface of components, so you don’t reference the dependencies explicitly—you reference them via interfaces.
2. Services and Providers
Angular’s Provided Services:
Service | Description |
---|---|
Http | For HTTP communication with the server |
Form | Form handler code |
Router | Page navigation code |
Animation | UI animations |
Library | For example, NgBootstrap |
3. Services
3.1 @Injectable() Annotation
When you write services, you typically write them as TypeScript classes, with one file (filename.service.ts) per class. It’s a good idea to mark these classes as injectable using the @Injectable()
annotation. @Injectable() marks a class as available to an injector for instantiation.
// car.service.ts
import { Injectable } from '@angular/core';
@Injectable()
export class CarService {
constructor(){
console.log('CarService: constructor');
}
// Some dummy method.
isSuperCharged(car: string){
return car === 'Ford GT' ? 'yes' : 'no';
}
}
3.2 Using Services
Import the service, add it to providers and inject it in constructor.
// app.component.ts
import { CarService } from './car.service';
@Component({
selector: 'car',
template: `
<h3>
Is Supercharged:
</h3>
`,
styles: [],
providers: [CarService]
})
export class CarComponent implements OnInit{
@Input() name;
supercharged: string = '';
constructor(private service: CarService){}
ngOnInit(){
this.supercharged = this.service.isSuperCharged(this.name);
}
}
3.3 Best Practice
If multiple components reply on this service, then it will be referenced by many times, and there are several instances of this service. Actually, we can share one instance of the service in the whole angular application. All you need to do is to push service class to app component or to the app.module.ts to avoid creating duplicated service instances.
4. Providers
There are three types of providers:
- class providers
- factory providers
- value providers.
4.1 Class Providers
class Watch {
getTime(): string {
return new Date() + "";
}
}
class Seiko extends Watch {
getTime(): string{
return "Seiko Time:" + super.getTime();
}
}
@Component({
selector: 'app-root',
template: `
<h1>
{{watch.getTime()}}
</h1>
`,
styles: [],
providers: [{
provide: Watch,
useClass: Seiko
}]
})
export class AppComponent {
constructor(private watch:Watch){}
}
Notice the providers, useClass.
4.2 Factory Provider
providers: [{
provide: LoggingService,
useFactory: () => new LoggingService(LOGGING_USE_DATE);
]}
4.3 Value Provider
providers: [{
provide: 'language',
useValue: 'en'
}]
5. Injector API
If you want even more control over creating dependencies, you can access the Injector object directly. The Injector is a class in the Angular core package. It’s a dependency injection container used for instantiating objects and resolving dependencies.
import { Injector } from '@angular/core';
const injector = Injector.resolveAndCreate([Car, Engine, Tires, Doors]);
const car = injector.get(Car);
Another example.
import { Injector } from '@angular/core';
const injector = Injector.resolveAndCreate(
[
provide(Car, useClass: Car)),
provide(Engine, useClass: Engine)),
provide(Tires, useClass: Tires)),
provide(Doors, useClass: Doors))
]
);
const car = injector.get(Car);