Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

Angular resolves a dependency from the nearest provider it can reach. For a component or directive, that usually means checking the current and ancestor element injectors first, then searching the environment injector hierarchy. This model explains why a component can get its own service instance, why a local provider can override a root provider, and where to look when Angular reports a missing provider.

Angular’s injector hierarchies at a glance

Modern Angular applications primarily use two hierarchies. They serve different purposes: one associates providers with the application or an environment, while the other associates them with elements in the component tree.

Hierarchy Where providers are configured What it means for scope
EnvironmentInjector providedIn metadata, an InjectionToken factory, or ApplicationConfig.providers; additional environment injectors can also be created. Provides application- or environment-level dependencies. The root EnvironmentInjector is created during bootstrap; additional environments can be used for router scopes or dynamically loaded components.
ElementInjector A component or directive’s providers or, for a component, viewProviders. Associated with a DOM element. Angular creates these injectors implicitly, and they are empty unless providers are configured. Components and directives on the same element share an injector.
ModuleInjector NgModule.providers and providers reachable through its imports. Relevant to NgModule-based applications. Lazy-loaded NgModules can create child ModuleInjector hierarchies.

The platform injector is above the root injector, and the NullInjector is the final fallback. If no provider is found, resolution fails there unless the request is optional. See Angular’s guide to hierarchical injectors for the hierarchy details.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

How Angular searches for a dependency

For a request made by a component or directive, Angular searches from the requesting element outward. It checks the current ElementInjector, then its ancestors. If no provider matches, Angular returns to the requesting element and searches the EnvironmentInjector ancestry. In an NgModule-based app, the ModuleInjector hierarchy is also involved.

  1. Check the current element. A provider on the requesting component or directive’s element is considered first.
  2. Check ancestor elements. Angular walks up the element-injector tree until it finds a matching provider or reaches the end of that path.
  3. Check environment scope. If the element path has no match, Angular searches the relevant EnvironmentInjector ancestry (and the ModuleInjector hierarchy in NgModule-based applications).
  4. Fail at the NullInjector if still unresolved. An ordinary request then produces a missing-provider error; an optional request can instead return null.

The first provider Angular finds wins. A provider on a nearer component can therefore take precedence over a provider configured at application level. This is intentional: it lets a subtree customize a dependency without changing what other parts of the application receive.

Choose a provider scope that matches the instance you need

Application-wide sharing

Use a root-level provider when the service should be shared across the application and have application-level scope. A service can declare providedIn: 'root', or the application can configure providers through ApplicationConfig.providers. Angular notes that the providedIn approach supports tree-shaking of unused services. Application configuration can override a service’s providedIn: 'root' default.

Environment or route scope

Additional EnvironmentInjectors allow providers to be scoped to an environment, including router scopes or dynamically loaded components. Choose this when the dependency should not simply be treated as globally shared, but avoid assuming that every route or dynamically loaded component automatically creates a distinct scope: the relevant environment injector must be configured for that scope.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Component-subtree isolation

Configure a service in a component’s providers when that component and its descendants should resolve a component-associated instance. The instance belongs to that component’s ElementInjector and is destroyed when the component is destroyed. This is useful for state that should be isolated between component instances or shared only within a subtree. A provider on a directive also configures its element injector.

Values that are not classes

Use an InjectionToken for dependencies such as configuration objects, functions, or primitive values that do not have a class type to use as the token. A token can supply a factory, while providers can also be configured manually in component, directive, route, or application configuration.

Use resolution modifiers to control the search

Modifiers change what the default ancestor search does: whether it may return no result, where it begins, or how far it can travel. Angular documents four common modifiers:

  • optional changes missing-provider behavior: Angular returns null instead of failing when no provider is found.
  • self restricts the lookup to the current injector. Use it when the dependency must be provided locally rather than inherited.
  • skipSelf starts lookup at the parent injector, bypassing a provider on the current injector.
  • host limits how far lookup can travel at the host boundary, subject to Angular’s view and content visibility rules.

These modifiers are especially useful when a provider deliberately delegates to an ancestor, a component requires a local dependency, or an optional integration should not make the requesting code fail when absent. The precise lookup boundary can depend on the component’s view/content relationship; consult Angular’s hierarchical dependency injection guide when applying host.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Debug a missing provider or an unexpected duplicate

Angular’s troubleshooting guide describes the practical path as the current element injector, parent element injectors, the environment injector, and finally NullInjector. For a NullInjectorError, verify both that a provider exists and that the requesting component can reach it.

  • Confirm the token is provided. Check the service’s injectable declaration, token factory, or relevant providers array.
  • Check the requesting component’s path. A provider can exist but still be outside the requester’s reachable element or environment hierarchy.
  • Look for a nearer override. A local provider may intentionally replace the application-level instance.
  • Investigate duplicate “singleton” instances. Inspect component-level providers; registering a service there associates it with that component’s injector rather than relying only on the root instance.
  • Visualize the hierarchy. Angular DevTools includes an injector tree inspector that shows injector levels and their providers.

For Angular’s full troubleshooting guidance, see Debugging and troubleshooting DI. For provider configuration patterns, see Defining dependency providers.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.