Lazy loaded modules have their own root scope. Providers added to lazy loaded modules get an instance in that root scope instead of the root scope of the application.
If you add the provider to a module that is not lazy loaded, only a single instance at the application root scope will be created.
https://angular.io/docs/ts/latest/cookbook/ngmodule-faq.html#!#q-lazy-loaded-module-provider-visibility
Why is a service provided in a lazy loaded module visible only to that
module?
Unlike providers of the modules loaded at launch, providers of
lazy loaded modules are module-scoped.
When the Angular router lazy-loads a module, it creates a new
execution context. That context has its own injector which is a direct
child of the application injector.
The router adds the lazy module's providers and the providers of its
imported modules to this child injector.
These providers are insulated from changes to application providers
with the same lookup token. When the router creates a component within
the lazy loaded context, Angular prefers service instances created
from these providers to the service instances of the application root
injector.
https://angular.io/docs/ts/latest/cookbook/ngmodule-faq.html#!#q-why-bad
Why is it bad if SharedModule provides a service to a lazy loaded
module?
This question arose in the Angular Module chapter when we
discussed the importance of keeping providers out of the SharedModule.
Suppose we had listed the UserService in the module's providers (which
we did not). Suppose every module imports this SharedModule (which
they all do).
When the app starts, Angular eagerly loads the AppModule and the
ContactModule.
Both instances of the imported SharedModule would provide the
UserService. Angular registers one of them in the root app injector
(see above). Then some component injects UserService, Angular finds it
in the app root injector, and delivers the app-wide singleton
UserService. No problem.
Now consider the HeroModule which is lazy loaded!
When the router lazy loads the HeroModule, it creates a child injector
and registers the UserService provider with that child injector. The
child injector is not the root injector.
When Angular creates a lazy HeroComponent, it must inject a
UserService. This time it finds a UserService provider in the lazy
module's child injector and creates a new instance of the UserService.
This is an entirely different UserService instance than the app-wide
singleton version that Angular injected in one of the eagerly loaded
components.
That's almost certainly a mistake.
Prove it for yourself. Run the live example. Modify the SharedModule
so that it provides the UserService rather than the CoreModule. Then
toggle between the "Contact" and "Heroes" links a few times. The
username goes bonkers as the Angular creates a new UserService
instance each time.
https://angular.io/docs/ts/latest/cookbook/ngmodule-faq.html#!#q-why-child-injector
Why does lazy loading create a child injector?
Angular adds
@NgModule.providers to the application root injector ... unless the
module is lazy loaded. Then it creates a child injector and adds the
module's providers to the child injector.
This means that a module behaves differently depending on whether it
is loaded during application start or lazy loaded later. Neglecting
that difference can lead to adverse consequences.
Why doesn't Angular add lazy loaded providers to the app root injector
as it does for eagerly loaded modules? Why the inconsistency?
The answer is grounded in a fundamental characteristic of the Angular
dependency injection system. An injector can add providers until it is
first used. Once an injector starts creating and delivering services,
its provider list is frozen. No new providers allowed.
When an applications starts, Angular first configures the root
injector with the providers of all eagerly loaded modules before
creating its first component and injecting any of the provided
services. Once the application begins, the app root injector is closed
to new providers.
Time passes. Application logic triggers lazy loading of a module.
Angular must add the lazy loaded module's providers to an injector
somewhere. It can't added them to the app root injector because that
injector is closed to new providers. So Angular creates a new child
injector for the lazy loaded module context.