Angular
DI with Lazy loading

What is DI?

Dependency injection, or DI, is a design pattern in which a class requests dependencies from external sources rather than creating them.

How DI works?

When Angular creates a new instance of a component class, it determines which services or other dependencies that component needs by looking at the constructor parameter types.

How DI works?

When Angular discovers that a component depends on a service, it first checks if the injector has any existing instances of that service

DI tree

Angular Creates two injector trees. Module Injector tree and Element Injector tree.

 

Module Injector tree: root module and for every lazy loaded module.

Element Injector tree is for DOM elements like Components & Directives.

Module DI tree

Elements DI tree

What is lazy loading?

Lazy Loading - a design pattern that loads NgModules as needed. Lazy loading helps keep initial bundle sizes smaller, which in turn helps decrease load times.

How is lazy loading related to DI?

For every lazy-loaded module Angular creates an injector which is inherited from parent injector.

What can go wrong?

Example project

We have a project with 3 modules:

 

* Core module
* Lazy module
* Lazy module 2
 

Core module

Core module contains CoreService and CoreInterceptor

Lazy module

Lazy Module contains a base router with a single router and component for that route and its own LazyInterceptor (just empty interceptor like Core one)

Lazy 2 module

Lazy 2 Module is very similar to Lazy Module but with 1 key difference - lazy 2 doesn't provide any providers.

Lazy 2 logs

In the console, we can see that in lazy 2 module injected array of 1 single interceptor - CoreInterceptor. The same array we can see in coreService. Nothing special, seems like expected behaviour

Lazy logs

From this part things became unexpected. As we can see Lazy module injected an array of interceptors with the LazyInterceptor, but the coreService has a different array of interceptors. Also why LazyModule contain only LazyInterceptor and not LazyInterceptor and CoreInterceptor ?

Lazy logs explanation

So why does this happen? All because of DI, and if we track the DI tree from previous slides we can understand what is going on.

First of all, we need to remind ourselves that each module has its own injector. So if we provide an interceptor in the lazy module it will create an array of interceptors in a lazy module injector and next time our lazy module component or service tries to get this array of interceptors it will look for it in their module, in our case - lazy module injector. Because our lazy module injector has overridden interceptors array with new interceptor we see only [LazyInterceptor] array

Lazy logs explanation

The second question that why coreService does not have the same array of overridden interceptors as LazyModule does?

The second thing about DI is when a lazy module injector doesn't find a provider he will ask about it in the parent injector (root or another lazy module).

So in our lazy module, we don't have CoreService so the lazy module injector asks about service in his parent injector (root). Parent injector has service but he will resolve this dependency with its own scope and will provide interceptors array from itself, not lazy module.

More info

Test project

Thanks for your attention