An injector maintains a registry of dependency providers and resolves requested tokens to their values. The core workflow has two operations: register a dependency under a token, then retrieve it when needed. section 3.1.2
Listing 3.1 Injector class for a naive implementation of a DI mechanism
class InjectionToken<T> { #1
constructor(public value: T) {}
}
class Injector {
private readonly dependencies = new Map<
InjectionToken<any>, any
>(); #2
provide<T>(dependency: T): InjectionToken<T> { #3
const token = new InjectionToken(dependency);
this.dependencies.set(token, dependency);
return token;
}
inject<T>(token: InjectionToken<T>): T { #4
const dependency = this.dependencies.get(token) as T;
return dependency;
}
}
A minimal Injector pairs InjectionToken instances with dependency values through provide and inject methods. section 3.1.2
Angular uses the same abstraction. Its injectors maintain token-based registries and supply dependencies to components, directives, and pipes, commonly through constructor parameters. This hides lookup code while retaining the same provide-and-retrieve mechanism. section 3.1.3
Injector organization changes as an application grows:
- The root injector is created globally and holds services registered with providedIn: 'root'. section 3.1.3
- Angular can create additional injectors, each with its own dependency registry and injection function. section 3.1.3
- Before standalone applications, ElementInjector handled DOM-node and component providers, while ModuleInjector supported NgModule-level providers. section 3.1.3
- Lookup is hierarchical: Angular searches injector registries through the applicable element, route, and application configuration scopes. section 3.1.3
A small provider-and-consumer chain
A primitive injector provides an HttpClient under an InjectionToken. UserService retrieves that token, calls http.get, and is then provided and retrieved through its own token. Angular applies the same relationship while resolving declared dependencies. section 3.1.2
