Angular DI system unfolded thoroughly in simple terms for practical use

Ankit kaushik
3 min readFeb 5, 2023
Photo by JOHN TOWNER on Unsplash

How do we create an instance

myInstance = {};
instanceOutOfClass = new MyClass(...dependencies);
property = 5;
member = 'value';

Instances could be simply created this way but to lazily create these instances (at the time of the request, the first time it’s requested through DI), also providing dependencies to those instances at runtime easily and to facilitate providing the same (singletons) throughout the hierarchy Angular has something called Providers which we usually call Services.

What can act as a Provider

Any Class having the Injectable decorator

@Injectable()
export class MyClass {}

// providing it,
// its instance will sit at the root unless provided again
@Injectable({provideIn: 'root'})
export class MyClass {}

// or providing it at a particular level
providers: [MyClass]

Injection Tokens

export const MY_INJECTION_TOKEN = new InjectionToken<MyType>('my.token');

// providing it
export const MY_INJECTION_TOKEN = new InjectionToken<MyType>('my.token', {
provideIn: 'root'
});

// providing the blueprint too
// factory function will be invoked to retrieve it's instance
export const MY_INJECTION_TOKEN = new InjectionToken<MyType>('my.token', {
factory: () => {
const otherInstance = inject(MyOtherInstance); // utilising the DI
// return () => otherInstance.token;
// return otherInstance.getToken(); // sync call
// return anything here you want use as an instance;
return {token: otherInstance.token, id: 5};
}
})

Providing the definition or value for a provider later

Even string tokens can be used as a provider (NOT suggested)

Better use injection token instances as it’s easier to reference and for type safety (the modern inject function keep the type definition intact).

// provide static value for this token
providers: [{provide: 'SOME_TOKEN', useValue: localStorage}]

// use MyClass to create & provide an instance for this token
providers: [{provide: 'SOME_TOKEN', useClass: MyClass}]

// call myFactory function to retrieve an instance for this token
const myFactory = () => {x: 5, y: 8};
providers: [{provide: 'SOME_TOKEN', useFactory: myFactory}]

// use an existing instance of MyClass whenever providing for this token
providers: [{provide: 'SOME_TOKEN', useExisting: MyClass}]
// Using that string token in the DI
const myStringToken = inject('SOME_TOKEN');

// or in the constructor
constructor(@Inject('SOME_TOKEN') myStringToken) {}

The first time angular finds a request for these classes to be injected, their instances are created and stored in its injector service for future references in other places.

/**
* These must be called from an injection context
* such as a constructor, a factory function, a field initializer,
* or a function used with `EnvironmentInjector#runInContext
*/

// instance is requested through DI
const myClassInstance = inject(MyClass);

// instance is requested through DI
constructor( private myClassInstance: MyClass) {}

Instances that angular creates from template and stores in it’s injector at that hierarchy

Any time we place a component, directive, or pipe inside our template an instance is created for that class at that level.


<my-component></my-component> // instance created for MyComponent Class

<p myDirective></p> // instance created for MyDirective Class

<span>{{ 'some content' | myPipe}}</span> // instance created for MyPipe Class

<ng-container *ngIf="!!exp"></ng-container> // instance created for NgIf Class
Instance classes of a few directives and components marked beside their usage in the DOM tree in Angular

and we can access all these instances if we are inside an injection context ( in the field initializer or constructor within your components, directives, pipes, services, etc) according to where they are placed lexically in your application hierarchy.

For example, within SideNavComponent we can get access to the instance of MainComponent (parent, lexically placed above in the hierarchy) like:

@Component({selector: 'ipm-side-nav'})
export class SideNavComponent {
constructor(private mainComponent: MainComponent) {}
}

In the next blog post, we’ll cover awesome applications of using the DI in Angular applications for various use cases.

Thanks for that like by the way😉, please feel free to connect for any feedback, query or just to say Hi! on my LinkedIn

--

--