What Is a Dependency?
When module X in an application needs module Y to
run, then module Y is a dependency of module X.
What Is Dependency Injection (DI)?
Dependency Injection is a powerful pattern for
managing code dependencies. DI is a way to create objects that depend upon
other objects.
Angular has its own DI framework pattern, and you
really can't build an Angular application without Dependency injection (DI).
A DI system supplies the dependent objects when
it creates an instance of an object.
Let us take an example of a CAR. The CAR consists
of following things -
1. Wheel
2. Headlight
3. Outer
door
4. Inner
door
5. Glass
6. Window
7. Fuel
level sensor
So to complete the CAR, we need those eight and
so many things.
In this example, we need to require total seven and
many more classes to build a fully functional CAR.
1. Car
class
2. Wheel
class
3. Headlight
class
4. Outer
door class
5. Inner
door class
6. Glass
class
7. Window
class
8. Fuel
level sensor class
Let’s
see what happen, without Dependency Injection (DI)
–
To complete the CAR class, we need to import all eight
classes here and make one fully functional CAR.
Now, here we have created eight classes instance
in the constructor of CAR class.
Note that, the CAR class is totally dependent on
these eight classes. Otherwise, it will not complete the CAR.
We are creating the instances in the CAR constructor.
So Wheel, Headlight, Outer door, Inner
door, Glass, Window, and Fuel are not decoupled from the CAR class.
Let’s
see what happen, with Dependency Injection (DI)
–
If we are using Dependency Injection then, we do
not need to create the instances in the constructor.
First, we need to provide all the dependencies to
the “app.module.ts” class -
import
{ BrowserModule } from
'@angular/platform-browser';
import
{ NgModule } from
'@angular/core';
//Import App Component
import
{ AppComponent } from
'./app.component';
//Import CAR classes.
import
{Wheel} from
'./car/wheel';
import
{Headlight} from
'./car/headlight';
import
{Glass} from
'./car/glass';
import
{InnerDoor} from
'./car/inner-door';
import
{OuterDoor} from
'./car/outer-door';
import
{Window} from
'./car/window';
import
{FuelLevel} from
'./car/fuel-level';
//AppModule class with @NgModule
decorator.
@NgModule({
//Static, This is the compiler configuration
//declarations is used for configure the selectors.
declarations: [
AppComponent,
],
//Composability and Grouping
// imports used for composing NgModules together.
imports: [
BrowserModule
],
//Runtime or injector configuration
//providers is used for runtime injector
configuration.
providers: [Wheel,
Headlight, Glass,
InnerDoor, OuterDoor,
Window, FuelLevel],
//bootstrapped entry component
bootstrap: [AppComponent]
})
export
class AppModule
{ }
In providers array, we need to provide all eight
dependencies.
Then, In the CAR class, inject those dependencies
into CAR constructor –
import
{Wheel} from
'./car/wheel';
import
{Headlight} from
'./car/headlight';
import
{Glass} from
'./car/glass';
import
{InnerDoor} from
'./car/inner-door';
import
{OuterDoor} from
'./car/outer-door';
import
{Window} from
'./car/window';
import
{FuelLevel} from
'./car/fuel-level';
export
class Car
{
constructor(public
wheel: Wheel,
public
headlight: Headlight,
public
glass: Glass,
public
innerdoor: InnerDoor,
public
outerdoor: OuterDoor,
public
window: Window,
public
fuellevel: FuelLevel)
{}
}
When CAR instance is created at that time, also
all the other instances of other classes are also created.
What Is Dependency Injection pattern?
DI is an application design pattern and you really
cannot build an Angular application without dependency injection (DI).
For more detail, refer the above questions.
What Is Injectors?
A service is just a class in Angular until you
register with an Angular dependency injector.
The injector is responsible for creating angular
service instances and injecting them into classes.
You rarely create an injector yourself and Angular
creates automatically during the bootstrap process.
Angular doesn't know automatically how you want
to create instances of your services or injector. You must configure it by
specifying providers for every service. Actually, providers tell the injector
how to create the service and without a provider not able to create the
service.
Bootstrap defines the components that should be
bootstrapped when this module is bootstrapped. The components listed here will
automatically be added to entryComponents.
Explore in detail by using above questions, What Is an entryComponents?
What Are @Injectable providers?
The @Injectable decorator identifies services and
other classes that are intended to be injected. It can also be used to
configure a provider for those services.
To inject the service into a component, Angular
provides an Injector decorator: @Injectable().
A provider defines the set of injectable objects
that are available in the injector of this module.
The @Injectable decorator marks a class as
available to an injector for instantiation. An injector reports an error when
trying to instantiate a class that is not marked as @Injectable.
Injectors are also responsible for instantiating
components. At the run-time the injectors can read class metadata in the
JavaScript code and use the constructor parameter type information to determine
what things to inject.
Injectable decorator and metadata -
@Injectable({
providedIn?: Type<any> | 'root' | null
factory: () => any
})
To inject the service into a component, Angular
provides an Injector decorator: @Injectable().
Here we configure a provider for CustomerService
using the @Injectable decorator on the class.
We have the following steps to create a Service-
1. Create
the service class
2. Define
the metadata with a decorator
3. Import
what we need.
In the above example, providedIn tells Angular
that the root injector is responsible for creating an instance of the
CustomerService.
The Angular CLI sets up provider automatically
when you generating a new service.
Why @Inject()?
The @Inject is a special technique for letting
Angular knows that a parameter must be injected.
Inject decorator and metadata-
@Inject({
token: any
})
When @Inject () is not present, Injector will use
the type annotation of the parameter.
import { Component, OnInit, Inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Component({
selector: 'app-customer',
templateUrl: './customer.component.html',
styleUrls: ['./customer.component.css']
})
export class CustomerComponent implements OnInit {
constructor(@Inject(HttpClient) private http) {
// use this.http which is the Http
provider.
}
ngOnInit(){ }
}
At this point, @Inject is a manual way of
specifying this lookup token, followed by the lowercase http argument to tell
Angular what to assign it against.
What Is Hierarchical Dependency Injectors?
Angular has a Hierarchical Dependency Injection
system. There is actually a tree of injectors that parallel an application's
component tree. You can reconfigure the injectors at any level of that
component tree.
What Is Injector tree?
In the Dependency Injection guide, you learned
how to configure a dependency injector and how to retrieve dependencies where
you need them.
An application may have multiple injectors. An
Angular application is a tree of components. Each component instance has its
own injector. The tree of components parallels the tree of injectors.
Three level component tree –
What Is Injector bubbling?
Refer the above questions.