Dependency Injection (DI) is not just a way to get services into components; it's a powerful tool for managing state and behavior across your application tree.
1The Singleton Pattern
When you use providedIn: 'root', you are opting into the most common and efficient DI pattern. Angular's compiler creates a single instance of the service the first time it's requested and shares that same instance everywhere. This is perfect for stateless utilities or global state managers. Furthermore, providedIn: 'root' makes your service 'tree-shakeable', meaning if no part of your app actually uses it, the service won't be included in your final production build.
2Local Overrides
There are times when a singleton isn't enough. Perhaps you have a 'Tabbed Interface' where each tab needs its own isolated state. By providing a service at the component level, you create a new 'Branch' in the DI tree. Any child components of that tab will receive the tab-specific instance, while the rest of the app continues to use the global one. This hierarchical shadowing is a sophisticated way to manage localized complexity without polluting the global scope.
