Where Can I Find the Sample Code for this Article?
Please check out the code at https://github.com/techcoaching/angular2.git in "feature/angular2-component" branch.
What is Component in Angular2?
In general view, for ease of understanding, a component is a page we want to display.
Do I Need to Register with Angular When Displaying My Component/ Page?
Yes, In userRoute.ts, we register with Angular which component will be rendered when navigating to a specified URI.
For example:
{ path: 'users', component: Users }
This will tell Angular when user navigates to "<rootUrl>/users", the Users
component will be displayed and the list of users were rendered to browser as below:
Ok, In the Code, We Just Register a ts Class (named Users). How Can Angular Know Which UI Should Be Rendered?
I see, if we look deeper into the definition of Users
class:
@Component({
templateUrl: "src/users.html"
})
export class Users {}
As in definition of Users
class, there is @Component
decorator with templateUrl
as property of parameter object.
@Component({
templateUrl: "src/users.html"
})
Angular will use content of "src/users.html" as the markup of Users
component.
Is There Any Other Way to Specifying the Content of HTML of Component?
Yes, there are 2 ways for specifying the markup of angular component:
- Using
templateUrl
: This is the path to HTML file that contains markup for that component. - Using:
template
: This contains markup of component as string
.
I change the @Component
decorator as below:
@Component({
template: "<div>this is inline template</div>"
})
And the "<root url>/users
" was rendered as below:
Should I Use template or templateUrl for My Component?
It is up to you. But I suggest, if your component needs short HTML (2 or 3 lines), yes, we can use template
property. Otherwise, we should use templateUrl
property.
How Can I Construct the Path to Template File in Angular Component?
For example, with Users
component, the path is "src/users.html".
The path was calculated from the root of application. It is the same folder with index.html in this case.
Please be aware that we have "<base />
" tag in index.html file. This tag defines how the app resolves the path to specified resource (HTML, CSS, images, ....).
What About "selector" Property?
Please ignore this property for now, we will come back to this in "Angular2 - Directive" article.
How Do We Display Data in HTML template File?
We need to define "users
" property in Users
class. This property contains user information that will be displayed on UI.
These steps show you how to display data in HTML file as below:
In users.html file:
<div>
<table class="table">
<thead>
<tr>
<th>#</th>
<th>First Name</th>
<th>Last Name</th>
<th>Username</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let user of users">
<th scope="row">{{user.id}}</th>
<td>{{user.firstName}}</td>
<td>{{user.lastName}}</td>
<td>{{user.userName}}</td>
<td>
<a title=""><i class="fa fa-edit"></i></a>
</td>
</tr>
</tbody>
</table>
</div>
Note: In HTML file, we can access public
properties (such as: users
in this case) in ts
class.
In users.ts:
@Component({
templateUrl: "src/users.html"
})
export class Users {
public users: Array<any> = [
{id:1, firstName:"Tu", lastName:"Tran", userName:"tutran"},
{id:2, firstName:"Tu 1", lastName:"Tran", userName:"tutran2"},
{id:3, firstName:"Tu 2", lastName:"Tran", userName:"tutran3"}
];
}
In ts
class, we need to declare it as public
property and set value to that property.
I Have Tried to Declare Users as Private Property. It Still Works. What is Wrong with My Code?
No, your code is correct.
Open users.js in the same folder with users.ts. I found that in both (declare users as public
or private
), the generated js is the same:
Users = (function () {
function Users() {
this.users = [
{ id: 1, firstName: "Tu", lastName: "Tran", userName: "tutran" },
{ id: 2, firstName: "Tu 1", lastName: "Tran", userName: "tutran2" },
{ id: 3, firstName: "Tu 2", lastName: "Tran", userName: "tutran3" }
];
}
return Users;
}());
That is why public
or private
does not impact the functionality of the page. I guest this is a mistake of typescript compiler (tsc).
At This Time, We Define the List of Users in Users.ts Class. How Can I Extract This Logic to External Class and Call It from users.ts File?
OK, for this, we need to define a new class (named UserService
) and call it from Users
class as below:
Define getUsers
method in userService.ts:
export class UserService {
public getUsers(): Array<any> {
let users: Array<any> = [
{ id: 1, firstName: "Tu", lastName: "Tran", userName: "tutran" },
{ id: 2, firstName: "Tu 1", lastName: "Tran", userName: "tutran2" },
{ id: 3, firstName: "Tu 2", lastName: "Tran", userName: "tutran3" }
];
return users;
}
}
And call it from Users
class (in users.ts):
export class Users {
private users: Array<any> = [];
constructor() {
let userService = new UserService();
this.users = userService.getUsers();
}
}
We also need to import UserService
class on the top of Users
class using import
statement:
import { UserService } from "./userService";
By This Way, I Need to Create a New Instance of User Service Manually, Can Angular Create For Me?
Yes, in Angular, we have Dependency Injection (DI) concept. It means Angular will look up the necessary class and create appropriated instance for you. Then, we can call appropriated method on that instance.
We also need to declare dependency between Users
and UserService
class.
Ok, Sound Interesting. Can You Show Me How to Use DI in Angular?
Ok, first of all, we need to change Users
class (in users.ts) as below:
export class Users {
private users: Array<any> = [];
constructor(userService: UserService) {
this.users = userService.getUsers();
}
}
Note
- In the constructor of
Users
class (in users.ts), we declare userService
with type of UserService
. - And call
getUsers()
of this userService
to get the list of users without creating a new instance of UserService
anymore.
If we run the app again. Angular will raise an error as photo:
Angular does not understand what is UserService
. To fix this, we need to add "providers
" in @Component
decorator as below:
@Component({
...,
providers: [UserService]
})
So the completed code for Users
at this time is as below:
@Component({
selector: "users",
templateUrl: "src/users.html",
providers: [UserService]
})
export class Users {
private users: Array<any> = [];
constructor(userService: UserService) {
this.users = userService.getUsers();
}
}
Let's compile and run again, the app runs well without any error:
This is Good. But I Do Not Want to Register in All My Components using UserService Class. Is There Any Other Solution?
Yes, in the application, we can have 1 or multiple modules (declared using @NgModule
decorator). Each component was registered in specified module.
So we can declare the UserService
in @NgModule
in which Users
component was declared.
Open UserModule
class (in userModule.ts), add providers
property as below:
@NgModule({
imports: [
BrowserModule,
FormsModule,
UserRoutes
],
declarations: [Users, CreateUser, EditUser, Layout],
bootstrap: [Layout],
providers: [UserService]
})
Now, in Users
, we can remove providers
from @Component
decorator:
@Component({
selector: "users",
templateUrl: "src/users.html"
})
Compile and refresh the browser again, the users page runs well:
I'm Still Not Happy with the Code, As We Use Directly to UserService Class. I Expect the App Uses Functions of UserService through IUserService Interface. How Can I Do It?
No, we cannot inject interface as dependency:
export class Users {
private users: Array<any> = [];
constructor(userService: IUserService) {
}
}
This will not work, as the interface is a typescript definition, JavaScript does not understand interface. These interfaces will be removed when compiling typescript code into JavaScript code in order to run the app.
So at run time, there is no interface for Angular using.
CodeProject
Thank you for reading.
Note: Please like and share with your friends if you think this is a useful article, I would really appreciate it.