It appears that there is some confusion between typescript parameter properties and the Angular2 Dependency Injection (DI) mechanism.
When defining constructors in typescript, you have two options. In short:
@Component()
class MyComponent {
constructor(
public dependency1: Dependency1,
public dependency2: Dependency2,
public dependency3: Dependency3
) { }
// [...]
This will be transformed into a longer, more verbose version that 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;
}
// [...]
You can find more information about this in the Typescript Handbook.
Both approaches have their advantages and disadvantages; the longer version is clearer for those new to typescript, while the shorter version saves typing but may be less clear. Choose according to your preference. :)
Note: If your class already has another decorator, you don't need to use the @Injectable() decorator. It only generates metadata for decorated classes to prevent excessive code bloat in transpiled code.
Regarding the Dependency Injection mechanic, assuming you have imported definitions for Dependency1, Dependency2, and Dependency3 (1), registered their providers (2), and angular created an instance of your component (3):
Within someFunction()
, this.dependency1
is declared as public at the start of your class and has a defined type (Dependency1
). However, its current value is undefined
since it was not assigned a value in the constructor or elsewhere.
Fix: Add dependency1: Dependency1
parameter to the constructor and assign this.dependency1 = dependency1
. You can also use the typescript shorthand mentioned above. ([EDIT]
constructor(public dependency1, ...
).
Accessing this.dependency2
inside the constructor should trigger a compile error because MyComponent
does not have a property named dependency2
. If this compiles/transpiles without errors, then this.dependency2
should have the correct value, although type information may be lacking (not ideal).
Fix: Declare public dependency2: Dependency2;
before the constructor. Alternatively, add the public
property in front of the constructor's dependency2
parameter. [EDIT] Or use
constructor(public dependency2, ...
).
Lastly, this.dependency3
faces both issues of lack of declaration (similar to dependency2
) and having an undefined value (like dependency1
).
Fix: Refer to the previous solutions mentioned. :)
Note: Understanding the difference between declaration & definition (Even though this reference is about C++, the concept applies to typescript) is crucial for this topic. I hope I did not confuse the terms while typing. ;)
This can be accomplished by:
import { Dependency1 } from 'dependency-module-1';
import { Dependency2 } from 'dependency-module-2';
import { Dependency3 } from 'dependency-module-3';
There are several ways to define providers for your dependencies:
During bootstrapping. This makes the modules available (via their providers) to every component in your app.
bootstrap(AppComponent, [
Dependency1,
Dependency2,
Dependency3
]);
Using the component decorator. This provides the modules to MyComponent and shares the same module instance with each child component.
If a child component has its own provider array, a new module instance is created for it (shared with all child components). For further reading, check out the official Angular2 guide and this blog post. Here is an example:
@Component({
providers: [
Dependency1,
Dependency2,
Dependency3
]
})
class MyComponent {
\\ [...]
Either MyComponent is passed as the first parameter to angular's bootstrap method, or the component's selector is used somewhere in your DOM.
For best practices, consider using tslint and custom rule plugins like codealyze which are angular-specific.
Lastly, refer to the official Angular Typescript Styleguide for additional guidance. :)