This code is not going to work unless I include SomeServiceClass in providers array. I tried input1: string. It doesn't work neither! I believe it is totally valid typescript code. The problem must be the way angular2 instantiates the component classes.
The reason you need to add
SomeServiceClass
to a providers
array of somengModule
is simple. Angular chose not to perform any dependency analysis based on code structure. They could have used the ES Module import in the injecting component to resolve the dependency and auto register it.
As evidence, Aurelia does exactly that.
In other words, in Aurelia, the following is sufficient to inject and use a dependency
import {autoinject} from 'aurelia-framework';
import {SomeServiceClass} from '...';
@autoinject export class MyComponent {
constructor(input1: SomeServiceClass) {}
}
And it works.
How are component classed actually instantiated by Angular?And, since I never instantiate service class, I can't imagine which service class instance is passed to component constructor.
A class in JavaScript is really a constructor function, and a constructor function is really a glorified factory function, and a factory function is really a glorified function.
What does that mean?
It means that Angular (and most DI frameworks) simply create an instance of the service when it is first requested by calling the provided class with
new
.
A simplified version of such code might look like
function instantiateComponent<T>(Component: new (...args: any[]) => T) {
const requiredDependencies = resolveDependencies(Component);
return new Component(...requiredDependencies);
}
function resolveDependencies(Dep: new (...args: any[]) => any): object[] {
const requiredDependencies = metaDataUtils.getDependencies(Dep);
return requiredDependencies.map(Dep => {
const alreadyCreatedDependency = injectorCache.find(dep => dep instanceof Dep);
if(alreadyCreatedDependency !== undefined) {
return alreadyCreatedDependency;
}
else {
const requiredDependencies = metaDataUtils.getDependencies(Dep);
// resolution is recursive
const newDependency = new Dep(...requiredDependencies.map(resolveDependencies));
injectorCache.push(newDependency);
return newDependency;
}
});
}
Does angular create one instance of service to be used by every component or it generates one instance for each component?
This one is tricky, by default only one instance is created per application.
This is an application level singleton and all components requesting it receive the same instance.
However, there are different implicit and explicit behaviors in Angular as well.
If a lazily loaded
ngModule
lists the same service in its providers
array, a second instance will be created and that instance will be shared by children of the lazily loaded module. That is actually an over simplification but it is already confusing and, given that whether or not a module is loaded lazily is determined by its consumers, it can lead to serious and subtle bugs.
Finally, a component may explicitly declare its own
providers
in the @Component
metadata. Any component doing so have will have a new instance of the service created and shared by all instances of that component.
Note: Just to be specific when you say
I noticed type of component constructor parameters must be a dependency.
you are not strictly correct. Dependencies are not types, they are values. TypeScript types are completely erased at compile time and what actually gets injected are values. A class is a value
class C {}
but TypeScript also infers the existence of some types from its declaration. But what is injected is the value
C
, not the type C
.
No comments:
Post a Comment