Reputation: 9533
I am seeing various versions of how to inject dependencies into my component, some are working for me, others not. So I am wondering, what are the pros and cons, what is the recommended best practice and why are certain ones not working as expected. Here an example:
@Component()
@Injectable()
class MyComponent {
dependency1: Dependency1;
constructor(dependency2: Dependency2, dependency3: Dependency3) {
this.dependency2 = dependency2;
}
someFunction() {
// Which of those will work, which is the best practice,
//what are pros and cons
this.dependency1.get();
this.dependency2.get();
this.dependency3.get();
}
}
So based on this example:
Is version 1 (Dependency1) equivalent to version 2 (Dependeny2)? Or is version 1 only for TypeScript without any relevance for injection?
Is this line of code
this.dependency2 = dependency2;
necessary of will this be done "automagically" like assumed for Dependency3?
Thanks a lot
Upvotes: 0
Views: 196
Reputation: 210
It seems like you are mixing up typescript parameter properties and the Angular2 DI mechanic.
Basically you have two options when defining constructors with typescript. The short version:
@Component()
class MyComponent {
constructor(
public dependency1: Dependency1,
public dependency2: Dependency2,
public dependency3: Dependency3
) { }
// [...]
Which will be transformed into the longer, more verbose version you can also use:
@Component()
class MyComponent {
public dependency1: Dependency1;
public dependency2: Dependency2;
public dependency3: Dependency3;
constructor(
dependency1: Dependency1,
dependency2: Dependency2,
dependency3: Dependency3
) {
this.dependency1 = dependency1;
this.dependency2 = dependency2;
this.dependency3 = dependency3;
}
// [...]
This is explained in the Typescript Handbook. Both have their pro & cons, the longer version is more understandable and clear for people new to typescript, while the later saves quite some typing, so choose your poison. :)
Notice: You don't need the @Injectable() decorator if your class already has another decorator, it only serves to force meta-data generation by typescript, which it only does for decorated classes to prevent bloating transpiled code with unnecessary meta-data.
Regarding the DI mechanic, assuming you imported definitions and for Dependency1, Dependency2 and Dependency3 (1), registered their providers (2) and angular
created an instance of your component (3):
Inside someFunction()
this.dependency1
is a declared (as public at the start of your class) and has a defined type (Dependency1
) for all typescript cares. But its current value is undefined
, since it has not been assigned a value in the constructor (or anywhere else).
Fix Add dependency1: Dependency1
parameter to the constructor and assign this.dependency1 = dependency1
or use the typescript short-hand above ([EDIT] constructor(public dependency1, ...
).
Accessing this.dependency2
inside the constructor should raise a compile-error, since MyComponent
has no property dependency2
. In the unlikely case this does compile/transpile then this.dependency2
should have the correct value, althought you will not have type information for this.dependency2
(bad).
Fix: Add a declaration for this.dependency2
to your component, eg. add public dependency2: Dependency2;
above the constructor [EDIT] or add the public
property in front of the constructors dependency2
parameter, constructor(public dependency2, ...
).
Last but not least this.dependency3
has both of the above problems, no declaration (like dependency2
) and undefined
as values (like dependency1
).
Fix: See above. ;)
Note: It is important to understand the difference between declaration & definition (This is about C++, but applies to typescript as well) for this and I hope I did not mix up terms while typping. ;)
Done for example via:
import { Dependency1 } from 'dependency-module-1';
import { Dependency2 } from 'dependency-module-2';
import { Dependency3 } from 'dependency-module-3';
There are multiple ways to define providers for your dependencies
During bootrap. This makes the modules available (via their providers) to each and every component in your app.
bootstrap(AppComponent, [
Dependency1,
Dependency2,
Dependency3
]);
With the component decorator. This makes the modules available to MyComponent and the same module instance to each child-component. If a child component hovever has its own provider array, a new module instance is generated for it (and like above shared with all child components). Recommended reading material is the official Angular2 guide and this blog post. Here is the example:
@Component({
providers: [
Dependency1,
Dependency2,
Dependency3
]
})
class MyComponent {
\\ [...]
Either MyComponent is the one passed as (first) parameter for angulars bootstrap method or the components selector is used somewhere in your DOM.
Regarding best practices, you could try using tslint and the awesome custom-rule-plugins, eg. the codealyze one is angular-specific.
Last but not least there is also the official Angular Typescript Styleguide. ;)
Upvotes: 4
Reputation: 4013
Please look at official documentation
Also have a look at this blog
Angular is based on constructor based dependency injection.
In short that means constructor parameters are injected as dependency.
Car(e:Engine)
Then engine will be inject by DI in car instance.
Now all your code is Typescript ways of defining a constructor. Following 2 code are equivalent.
e: Engine
Car(e:Engine){
this.e = e
}
Car(public e:Engine){ }
Upvotes: 0