TU Tran

Technologies should serve for business purpose.

NAVIGATION - SEARCH

Angular2 - Component

Where can I find the sample code for this article?

Please checkout the code at https://github.com/techcoaching/angular2.git in "feature/angular2-component" branch.

What is component in Angular2?

In general view, For easier to understand that 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 that which component will be rendered when navigating to specified URI.

For example:

{ path: 'users', component: Users }

This will tell Angular when user navigate 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 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 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 need 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 aware that we have "<base />" tag in index.html file. this tag define 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 to 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 to the functionality of the page. I guest this is mistake of typescript compiler (tsc).

At this time, We define the list of users in Users.ts class. How can I extract thi logic to external class and call it from users.ts file?

ok, For this, we need to define 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 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 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 new instance of UserService anymore.

If we run the app again. Angular will raise error as photo:

As the 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 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 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 can not 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. 

What about styling for Angular2 component. As mention on angular.io site, we can specify style for each component separately?

Yeah, this is a nice feature that angular support for its component. By this way, Angular will generate appropriated class and inject into appropriated DOM.

With Users component (in users.ts), I will try to add style into the "th" tag as below:

and the output to browser will be:

Inspect the DOM, we see the th tag has new attribute and new css were applied to those elements:

 

Where can I find more articles in Angular2 series?

For learning Angular2, I think you should follow the list of articles as below (click on link to see detail page):

  • Overview: Introduce about Angular2
  • Routing: Understand how Angular2 navigate between pages/ components
  • Component: Learn about components/ pages in Angular2.
  • Binding: Learn how Angular2 show data on UI and receive input data from user.
  • Directive: Learn how to create re-usable component/ control that can be re-used across the application.
  • Integrate together: using what we learn about Angular for building the sample demo. 
  • Append "angular 2" into current "Angular 1" application.

Thank for reading.

Note: Please like and share to your friends if you think this is usefull article, I really appreciate

Add comment