An injector is a container with providers/services. It implements one method get
and returns an instance of the service. Let's implement the most basic version of the injector in JS pseudocode:
class ReflectiveInjector {
providers = [];
static resolveAndCreate(providers) {
providers.forEach((provider)=>{
providers.push({token: provider.provide, instance: new provider.useClass})
});
)}
get(dep) {
return providers.find((provider)=>{ return provider.token === token });
}
}
Now, we need to create the instance of the injector before we can use it. And when we create it we define providers:
const existingInjector = ReflectiveInjector.resolveAndCreate([{provide: A, useClass: A }]);
const AInstance = existingInjector.get(A);
So you see in order to use an injector it has to be created first. It doesn't have any specific methods that allow adding providers so after it's created we can't add any new providers to it.
The injector you inject into the component constructor is already created by Angular. You can't add anything to it and can query only the providers already defined on it. If you need to provide B
class, you can't do that with existing injector. You need a new one. And this is where ReflectiveInjector
class comes in. It allows you to add new providers by creating a new instance of the injector and registering new providers. And the good part is that it can also setup an injector chain.
Let's a bit modify resolveAndCreate
and get
methods to allow chaining injectors:
class ReflectiveInjector {
providers = [];
parent;
static resolveAndCreate(providers, parent) {
this.parent = parent;
...
}
get(dep) {
let found = providers.find((provider)=>{ return provider.token === token });
if (!found && parent) {
found = parent.get(dep);
}
return found;
}
So now the only that left is to use it:
// passing existingInjector as a parent
const childInjector = ReflectiveInjector.resolveAndCreate([{provide: B, useClass: B }], i);
const AInstance = childInjector.get(A);
const BInstance = childInjector.get(B);
Now, suppose someone might want to get access to our existingInjector
. We need a token to use to get this existing injector. Let's define this token like this:
abstract class Injector {}
and let's write a function that will get the existing injector:
function resolveDependency(token) {
if (token === Injector) {
return existingInjector;
}
}
And now suppose that Angular when executes the constructor of the component, uses the tokens you specified to get dependencies and passes them to the resolveDependency
function. So you write like this:
// the Injector here is a reference to our abstract Injector class and simply used as a token
MyComp {
constructor(private injector: Injector) { ... }
}
And the token here is Injector
which as I said is passed into the resolveDependency
function and the existing injector is returned.