The Dependency Injection in Angular 2
Trying to understand the intense hype behind the dependency injection in Angular 2.
If you are reading this article, then it's very likely that you have read my previous articles on Angular 2 . Up to now, you understand how to create a basic application with Angular 2 (including the form validations and home page). But, the procedures that enabled you to make the applications were very basic and may not be enough for advanced applications.
One feature that makes Angular 2 apart from other JavaScript frameworks/libraries is its cool dependency injection that is considered one of its biggest features and selling point. You already know about the Components (which are basically the building blocks of AngularJS 2!), so know that dependency injection allows you to inject dependencies (which are the requirements of your class/component) in your different components across your applications without you needing to know (or worry about) how those dependencies were created and what dependencies they need themselves!
The above highlighted definition of DI (Dependency Injection) is not sufficient for you, as it doesn't provide any concrete coding examples, so in this article I will tell you what dependency injection is and how to use it effectively in your code. Even though DI is available in AngularJS 1 as well, I will cover the AngularJS 2's DI only. Let's get started!
The Dependency Injection in AngularJS 2 - What's the hype?
Let's understand what DI really is. Consider the following code:
class School {
constructor() {
this.student = new Student();
this.teacher = Teacher.getInstance();
this.principal = app.get('principal ');
}
}
In our class "School" we have a constructor which contains some internal properties. You can see that I have defined them differently. The problem with this code is that it is not only hard to maintain, but also hard to test. For example, you can't test the code separately without its dependencies or let's say you want to replace its properties to something else (i.e., FirstName(), LastName(), etc.), then it's not possible to do so with this approach. Now, let's take a look at the following code:
class School {
constructor(student, teacher, principal) {
this.student = student;
this.teacher = teacher;
this.principal = principal;
}
}
Here, we have moved the dependencies to the constructor, so that whenever someone wants to create the class, he/she needs to supply the dependencies as well. This is called dependency injection (and it is also known as constructor injection). This not only allows you to define classes without the need for initializing dependencies, but also enables you to use the same Instance of the dependencies in several classes. Now, consider the following (JavaScript) code:
function main() {
var student = new Student();
var teacher = new Teacher();
var principal = new Principal ();
var school = new School(student , teacher, principal);
school .showData();
}
Here, we are initializing every class or dependencies of the "School" class in our main function and then passing their objects into the constructor of the "Student". But, DI allows to use the Injector Framework. Here's the way to write the above code differently: (note that we are using JavaScript in the example)
class School{
...
}
School.$inject = ['Student', 'Teacher', 'Principal '];
And in our main file:
var app = angular.module('myApp', []);
app.service('School', School);
app.service('OtherService', function (School) {
});
Basically, what we are doing is that we are using "Injector" to create a service, which contains every dependencies and thus can be used everywhere. This is the DI in AngularJS 1, but it has several issues:
- Internal cache - Singletons (https://en.wikipedia.org/wiki/Singleton_pattern) are used in the form of dependencies, which means it will create one instance of the class only whenever you ask at the service. It makes it VERY difficult to write complex code that relies on more than one instance of the class.
- Namespace collision - If you have instantiated the "School" service, then that service can be only considered one of the token of that type of service, meaning if there are libraries/frameworks that use the service with a similar name, then there would be a problem.
- Built into the framework - In Angular 1, DI can be used only within the framework, so you can't use it as a stand-alone library for your other JavaScript projects.
This is the basic concept of DI, but we need to understand how it actually operates in AngularJS 2.
Installing the AngularJS 2 CLI
In my previous articles, I have used the basic template from Github as the basis for writing the code examples, but this is not really a sophisticated way of creating projects and components. The CLI (command line interface) of the AngularJS 2 provides a very cool way to create new AngularJS 2 projects and add components with just simple lines of commands!
So, install it globally:
$npm install -g angular-cli
In order to create a new project and run the server. you do the following:
$ng new <project-name>
$cd <project-name>
$ng serve
If you navigate to http://localhost:4200/, then you can see the results. You can read more about here (https://cli.angular.io/).
School, Student, Teacher... and whatnot.
AngularJS 2 beautifully solved all the issues pertaining to a previous DI. Firstly, DI can be used as a stand-alone framework, so it can be used outside the AngularJS 2 environment. This allows you to seamlessly integrate several components together and even merge the code with different environments. Secondly, you can use different instances of the dependencies in your different components without them affecting each other!
Let's create two new components:
$ng g component student1
$ng g component student2
Now create a new class "School":
$ng g class school
Your 'src/app" directory should look like the following:
Open the "student1" folder and paste the following code in "student1.component.html" and "student1.component.ts":
student1.component.ts
import { Component } from '@angular/core';
import {School} from '../school'
@Component({
selector: 'app-student1',
templateUrl: './student1.component.html',
styleUrls: ['./student1.component.css'],
providers: [School]
})
export class Student1Component {
name: string;
teacher:string;
classno:string;
constructor(private _School: School){
}
addName(name:string) {
this._School.name = name;
this.name = name;
}
addTeacher(teacher:string) {
this._School.teacher = teacher;
this.teacher = teacher;
}
addClassno(classno:string) {
this._School.classno = classno;
this.classno = classno;
}
resetValues() {
this.name = "";
this.teacher = "";
this.classno = "";
}
}
student1.component.html
<h1>Student 1</h1>
<div>
<input type="text" #input1> <br><br>
<button (click)="addName(input1.value)">Add Name: </button>
<p>Name Adeed: {{name}}</p>
<input type="text" #input2> <br><br>
<button (click)="addTeacher(input2.value)">Add Teacher: </button>
<p>Teacher Adeed: {{teacher}}</p>
<input type="text" #input3> <br><br>
<button (click)="addClassno(input3.value)">Add Class No: </button>
<p>Class No. Adeed: {{classno}}</p>
<button (click)="resetValues()">Reset</button>
</div>
Now open the "student2" folder and paste the following code in "student2.component.html" and "student2.component.ts":
student2.component.ts
import { Component } from '@angular/core';
import {School} from '../school'
@Component({
selector: 'app-student2',
templateUrl: './student2.component.html',
styleUrls: ['./student2.component.css'],
providers: [School]
})
export class Student2Component {
name: string;
teacher:string;
classno:string;
constructor(private _School: School){
}
addName(name:string) {
this._School.name = name;
this.name = name;
}
addTeacher(teacher:string) {
this._School.teacher = teacher;
this.teacher = teacher;
}
addClassno(classno:string) {
this._School.classno = classno;
this.classno = classno;
}
resetValues() {
this.name = "";
this.teacher = "";
this.classno = "";
}
}
student2.component.html
<h1>Student 2</h1>
<div>
<input type="text" #input1> <br><br>
<button (click)="addName(input1.value)">Add Name: </button>
<p>Name Adeed: {{name}}</p>
<input type="text" #input2> <br><br>
<button (click)="addTeacher(input2.value)">Add Teacher: </button>
<p>Teacher Adeed: {{teacher}}</p>
<input type="text" #input3> <br><br>
<button (click)="addClassno(input3.value)">Add Class No: </button>
<p>Class No. Adeed: {{classno}}</p>
<button (click)="resetValues()">Reset</button>
</div>
Both of these components are using the "School" class, so open the "school.ts" and paste the following:
school.ts
export class School {
name:string;
teacher:string;
classno:string;
}
We need to use these components in our application, so open the "app.component.html" and paste the following code:
app.component.html
<h1>
<app-student1></app-student1>
<app-student2></app-student2>
</h1>
Now, if you do "ng serve" in the root directory, then you will see both the components working pretty well.
If you type something in the input fields and press the corresponding buttons, then you will see the results below the fields. Pressing the "Reset" button resets the values to the default (which is set to null).
So, now let's see what we did in this applicaiton.
The "School" class should be self-explanatory as we did nothing but defined the variables for our class which we are going to use in our student components. The "student1.component.ts" and "student2.component.ts" is very similar, so open the "student1.component.ts". In the class, you can see I have defined the local variables. Now, if you look at the constructor, you will see that I am injecting the "School" dependency in it.
constructor(private _School: School){
}
We have used "private" access modifier because we don't want our class to be called outside form its own class. This is not sufficient, as you need to assign the dependencies in the "providers", so that the program knows that the class is using a unique instance of the "School".
@Component({
...
providers: [School]
})
In the other methods of the class, I did nothing but defined several useful methods for setting (and getting) the values to/from the "School" class. The code in the "student1.component.html" is simply if you look at it, as I have defined simple form commands. The reason for creating two similar components is to show you how the dependency "School" is unique in both the components, so the values in one instance of the "School" doesn't affect the values in another instance of the "School". This is a power of DI in AngularJS 2.
Conclusion
So... finally you have successfully read about the DI (Dependency Injection) in AnguljarJS 2. The design pattern of DI has definitely made several programming tasks easier to do and manage. The new DI version in AngularJS 2 has also solved the previous problems in AngularJS 1 assuring you that development on DI will remain in the future.
If you have any questions, then please ask so in the comments section below!
Recent Stories
Top DiscoverSDK Experts
Compare Products
Select up to three two products to compare by clicking on the compare icon () of each product.
{{compareToolModel.Error}}
{{CommentsModel.TotalCount}} Comments
Your Comment