Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

Angular2 - Component

5.00/5 (7 votes)
18 Jan 2017Apache5 min read 15.7K  
Angular2 - Component

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:

JavaScript
{ 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:

Image 1

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:

JavaScript
@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.

JavaScript
@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:

JavaScript
@Component({
    template: "<div>this is inline template</div>"
})

And the "<root url>/users" was rendered as below:

Image 2

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:

HTML
<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:

JavaScript
@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:

JavaScript
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:

JavaScript
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):

HTML
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:

JavaScript
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:

JavaScript
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:

Image 3

Angular does not understand what is UserService. To fix this, we need to add "providers" in @Component decorator as below:

JavaScript
@Component({
    ...,
    providers: [UserService]
})

So the completed code for Users at this time is as below:

JavaScript
@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:

Image 4

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:

JavaScript
@NgModule({
    imports: [
        BrowserModule,
        FormsModule,
        UserRoutes
    ],
    declarations: [Users, CreateUser, EditUser, Layout],
    bootstrap: [Layout],
    providers: [UserService]
})

Now, in Users, we can remove providers from @Component decorator:

JavaScript
@Component({
    selector: "users",
    templateUrl: "src/users.html"
})

Compile and refresh the browser again, the users page runs well:

Image 5

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:

JavaScript
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.

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.

License

This article, along with any associated source code and files, is licensed under The Apache License, Version 2.0