Part 2: Create Angular 2 Components in TypeScript

In the previous tutorial, we created the initial structure of the Angular 2 Single-Page Application (SPA) in Typescript. In this tutorial, we will create Angular 2 Dashboard and Employees feature modules in TypeScript.

Task 1. Create the Dashboard Feature Module

dashboard.module.ts: is a feature module that own the Dashboard material. The root module named AppModule imports it as a supporting module.

dashboard.routing.ts: is a Router module which provides the configured services with routes in the Dashboard feature module

dashboard-resolve.service.ts: is a resolve service responsible for fetching data before rendering the Dashboard component

dashboard.service.ts: is an asynchronous service responsible for getting Dashboard data from the resolve service in the dashboard-resolve.service.ts file

dashboard.component.ts: is a component contains the properties and functions that are bound to the Dashboard view. It also contains the presentation logic for the Dashboard view, and is the glue between the data and the view.

dashboard.component.html: is an Angular 2 HTML template that defines the view for the Dashboard

Let’s add dashboard.module.ts file to the dashboard folder.

Replace the code in this file with the following:

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

import{NgModule}from'@angular/core';

import{nvD3}from'./ng2-nvd3';

/* Dashboard Feature Module */

import{DashboardComponent}from'./dashboard.component';

import{DashboardService}from'./dashboard.service';

import{DashboardResolve}from'./dashboard-resolve.service';

/* Dashboard Router */

import{dashboardRouting}from'./dashboard.routing';

@NgModule({

imports:[dashboardRouting],

declarations:[DashboardComponent,nvD3],

providers:[DashboardService,DashboardResolve]

})

exportclassDashboardModule{}

Line 13 imports a supporting module whose exported components, directives, or pipes are referenced by any component declared in the DashboardModule.

dashboardRouting: is a Router module with the additional route to display the DashboardComponent in this feature module

Line 14 declares a list of components (DashboardComponent, nvD3) that belong to the DashboardModule.

Line 15 provides a list of services (DashboardService, DashboardResolve) to any component declared in this DashboardModule.

Line 18 exports DashboardModule so that the AppModule in app.module.ts file can import it.

Task 2. Add routing to the Dashboard feature module

Add the dashboard.routing.ts to the dashboard folder.

Replace the code in this file with the following:

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

import{Routes,RouterModule}from'@angular/router';

import{DashboardComponent}from'./dashboard.component';

import{DashboardResolve}from'./dashboard-resolve.service';

const dashboardRoutes:Routes=[

{path:'dashboard',

component:DashboardComponent,

resolve:{

dashboard:DashboardResolve

}

}

];

export constdashboardRouting=RouterModule.forChild(dashboardRoutes);

The feature module named DashboardModule imports the dashboardRouting as a supporting module. Since we are in a feature module, we use RouteModule.forChild method to only register additional route with the Router.

When URL matches the path segment /dashboard, the Router will:

use resolve to retrieve data from DashboardResolve service

assign the result to dashboard before rendering the DashboardComponent

Add the dashboad-resolve.service.ts to the dashboard folder.

Replace the code in this file with the following:

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

14

import{Resolve,ActivatedRouteSnapshot}from'@angular/router';

import{Injectable}from'@angular/core';

import{Observable}from'rxjs/Observable';

import{DashboardService}from'./dashboard.service';

@Injectable()

exportclassDashboardResolveimplementsResolve<any>{

constructor(private dashboardService:DashboardService){}

resolve(route:ActivatedRouteSnapshot):Observable<any>{

returnthis.dashboardService.getSetting();

}

}

The feature module named DashboardModule imports the DashboardResolve as a service. We inject the DashboardService and implement the resolve method that supports an Observable. We also call our DashboardService.getSetting method that returns an observable to prevent our route from loading until the Dashboard data is fetched.

The feature module named DashboardModule imports the DashboardService as a service. The DashboardService imports RxJS Observable to get Dashboard data by sending an HTTP GET request to api/dashboard.json in the api folder. The code example provided by Angular 2 Developer Guide on how to handle HTTP Client errors is used here.

If you are interested in testing the Dashboard feature module with the Employee Tracker Web Api that you have created in another tutorial series, you can uncomment Line 9 and comment Line 8. Line 9 retrieves Dashboard data from http://localhost/employee-tracker-apis/api/dashboards.

Task 4: Add component and view to the Dashboard feature module

Add the component named dashboard.component.ts to the dashboard folder.

Replace the code in this file with the following:

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

import{ActivatedRoute}from'@angular/router';

import{Component,OnInit}from'@angular/core';

declarelet d3:any;

@Component({

selector:'et-dashboard',

templateUrl:'app/dashboard/dashboard.component.html'

})

exportclassDashboardComponentimplementsOnInit{

title='Dashboard';

positions:number;

offices:number;

employees:number;

lineChartOptions:{};

lineChartData:{};

pieChartOptions:{};

pieChartData:{};

constructor(private route:ActivatedRoute){}

ngOnInit(){

/* Initialize this.* bindable members with data.* members */

let data=this.route.snapshot.data['dashboard'];

this.positions=data.totalPositions;

this.offices=data.totalOffices;

this.employees=data.totalEmployees;

this.setupCharts(data);

}

privatesetupCharts(data){

// Configure options for the bar chart

this.lineChartOptions={

chart:{

type:'historicalBarChart',

height:500,

margin:{

top:40,

right:50,

bottom:60,

left:30

},

x:function(d){returnd.key;},

y:function(d){returnd.value;},

xAxis:{

axisLabel:'Years',

rotateLabels:30

},

yAxis:{

axisLabel:'Employees',

axisLabelDistance:-10

},

showLegend:false

}

};

// Bind data to the bar chart

this.lineChartData=[{

values:data.employeesPerYear,

color:'#7777ff',

area:true

}];

// Configure options for the pie chart

this.pieChartOptions={

chart:{

type:'pieChart',

height:500,

x:function(d){returnd.key;},

y:function(d){returnd.value;},

showLabels:true,

valueFormat:function(d){

returnd3.format(',.0f')(d)+' employees';

},

duration:500,

labelThreshold:0.01,

labelSunbeamLayout:true,

legend:{

margin:{

top:5,

right:35,

bottom:5,

left:0

}

}

}

};

// Bind data to the pie chart

this.pieChartData=data.employeesPerOffice;

}

}

The feature module name DashboardModule declares the DashboardComponent. The constructor function of the DashboardComponent expects an injectable parameter named ActivatedRoute. When our DashboardComponent activates, the ActivatedRoute.snapshot.data['dashboard'] contains the Dashboard data from the DashboardResolve service. We simply bind the this.* members to the component and used them in the view.

NOTE: We had already declared nvd3 directive in the feature module named DashboardModule.

<i class="fa fa-info-circle"></i><strong> Like Employee Tracker?</strong> Make sure you check out other tutorials in <a class="alert-link"href="http://cc28tech.com"target="_blank">Cathy Wun's Blog</a>!

Line 1 uses slideInRight in Animate.css to slide Dashboard view from right to left.

The following highlighted code uses Angular’s data-binding to wire this.* members and Dashboard view together:

Line 6 uses {{title}} to display the title of this screen.

Line 25 uses {{positions}} to display the total number of positions.

Line 42 uses {{offices}} to display the total number of offices.

Line 59 uses {{employees}} to display the total number of employees.

The following highlighted code uses nvd3 directive to wire this.* members and charts together:

Line 77 uses lineChartOptions to configure bar chart.

Line 77 uses lineChartData to populate bar chart with data.

Line 89 uses pieChartOptions to configure pie chart.

Line 89 uses pieChartData to populate pie chart with data.

Line 98 stops the “loading bars” animation after finished loading the Dashboard view.

Task 5. Create the Employees feature module

The Employees feature module contains the following files:

employees.module.ts: is a feature module that own the Employees material. The root module named AppModule imports it as a supporting module.

employees.routing.ts: is a Router module which provides the configured services with routes in the Employees feature module

employees-resolve.service.ts: is a resolve service responsible for fetching data before rendering the Employees component

employee.service.ts: is an asynchronous service responsible for getting a list of Employee data from the EmployeesResolve service in the employees-resolve.service.ts file

employee.model.ts: contains information of an employee

employees.component.ts: is a component contains the properties and functions that are bound to the Employees view. It also contains the presentation logic for the Employees view, and is the glue between the data and the view.

employees.component.html: is an Angular 2 HTML template that defines the view for the Employees

Let’s add employees.module.ts file to the employees folder.

Replace the code in this file with the following:

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

import{NgModule}from'@angular/core';

import{CommonModule}from'@angular/common';

import{DataTableDirectives}from'angular2-datatable/datatable';

/* Employees Feature Module */

import{EmployeesComponent}from'./employees.component';

import{EmployeeService}from'./employee.service';

import{EmployeesResolve}from'./employees-resolve.service';

/* Employees Router */

import{employeesRouting}from'./employees.routing';

@NgModule({

imports:[CommonModule,employeesRouting],

declarations:[EmployeesComponent,DataTableDirectives],

providers:[EmployeeService,EmployeesResolve]

})

exportclassEmployeesModule{}

Line 14 imports a supporting module whose exported components, directives, or pipes are referenced by any component declared in this EmployeesModule.

CommonModule: provides common directives

employeesRouting: is a Router module with the additional route to display the EmployeesComponent in this feature module

Line 15 declares a list of components (EmployeesComponent, DataTableDirectives) that belong to this EmployeesModule.

Line 16 provides a list of services (EmployeeService, EmployeesResolve) to any component declared in this EmployeesModule.

Line 19 exports EmployeesModule so that the AppModule in app.module.ts file can import it.

Task 6. Add routing to the Employees feature module

Add the employees.routing.ts to the employees folder.

Replace the code in this file with the following:

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

import{Routes,RouterModule}from'@angular/router';

import{EmployeesComponent}from'./employees.component';

import{EmployeesResolve}from'./employees-resolve.service';

const employeesRoutes:Routes=[

{path:'employees',

component:EmployeesComponent,

resolve:{

employees:EmployeesResolve

}

}

];

export constemployeesRouting=RouterModule.forChild(employeesRoutes);

The feature module named EmployeesModule imports the employeesRouting as a supporting module. Since we are in a feature module, we use RouteModule.forChild method to only register additional route with the Router.

When URL matches the path segment /employees, the Router will:

use resolve to retrieve data from EmployeesResolve service

assign the result to employees before rendering the EmployeesComponent

Add the employees-resolve.service.ts to the employees folder.

Replace the code in this file with the following:

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

import{Injectable}from'@angular/core';

import{Observable}from'rxjs/Observable';

import{Resolve,ActivatedRouteSnapshot}from'@angular/router';

import{Employee}from'./employee.model';

import{EmployeeService}from'./employee.service';

@Injectable()

exportclassEmployeesResolveimplementsResolve<Employee>{

constructor(

private employeeService:EmployeeService

){}

resolve(route:ActivatedRouteSnapshot):Observable<Employee[]>{

returnthis.employeeService.getList();

}

}

The feature module named EmployeesModule imports the EmployeesResolve as a service. We inject the EmployeeService and implement the resolve method that supports an Observable. We also call our EmployeeService.getSetting method that returns an observable to prevent our route from loading until a list of Employee data is fetched.

The feature module named EmployeesModule imports the EmployeeService as a service. The EmployeeService imports RxJS Observable to get a list of Employee data by sending an HTTP GET request to api/employees.json in the api folder. The code example provided by Angular 2 Developer Guide on how to handle HTTP Client errors is used here.

If you are interested in testing the Employees feature module with the Employee Tracker Web Api that you have created in another tutorial series, you can uncomment Line 11 and comment Line 10. Line 11 retrieves a list of Employee data from http://localhost/employee-tracker-apis/api/employees.

Task 8. Add data model to the Employee feature module

Add the employee.model.ts to the employees folder.

Replace the code in this file with the following:

JavaScript

1

2

3

4

5

6

7

8

9

10

11

exportclassEmployee{

id:number;

firstName:string;

lastName:string;

position:string;

office:string;

sex:string;

age:number;

startDate:string;

salary:number;

}

The Employee class contains information of an employee. We export the Employee class because we reference it in employees.component.ts, employee.service.ts, and employee-resolve.service.ts files.

Task 9: Add component and view to the Employees feature module

Add the component named employees.component.ts to the employees folder.

Replace the code in this file with the following:

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

import{ActivatedRoute}from'@angular/router';

import{Component,OnInit}from'@angular/core';

import{Employee}from'./employee.model';

@Component({

selector:'et-employees'

,templateUrl:'app/employees/employees.component.html'

})

exportclassEmployeesComponentimplementsOnInit{

errorMessage:string;

title='Employees';

employees:Employee[];

constructor(private route:ActivatedRoute){}

ngOnInit(){

/* Initialize this.* bindable members with data.* members */

this.employees=this.route.snapshot.data['employees'];

}

}

The feature module name EmployeesModule declares the EmployeesComponent. The constructor function of the EmployeesComponent expects an injectable parameter named ActivatedRoute. When our EmployeesComponent activates, the ActivatedRoute.snapshot.data['employees'] contains a list of Employee data from EmployeesResolve. We simply bind the this.* members to the component and used them in the view.

NOTE: We had already declared DataTableDirectives directive in the feature module named EmployeesModule.

We're using <a class="alert-link"href="https://github.com/mariuszfoltak/angular2-datatable"target="_blank">angular2-datatable</a> for the sort and paging function on the grid. Read the documentation for more customization options or feel free to use something else!

Prerequisites

Install Node.js and npm (Nods.js > 6.3.x and npm > 3.10.x) on your machine if you haven’t done so already

Task 1. Set up the Project Structure

I prefer to organize my project based on components/modules. Building a maintainable, scalable, and well-organized application should start from the beginning. Using DRY and SRP patterns, we will create smaller code files that each handle one specific job. This approach has helped me personally to locate, enhance, and debug requested features from clients quickly.

Let’s creating folders and copying files to C:\_tutorials\ng2-employee-tracker from GitHub as listed below:

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

ng2-employee-tracker// Project folder

|--api// test data

|--dashboard.json

|--employees.json

|--app

|--dashboard// Dashboard feature module

|--ng2-nvd3.ts// nvd3

|--employees// Employees feature module

|--layout// Layout component

|--content

|--css

|--animate.css

|--loading-bars.css

|--sb-admin-2.css

|--images

|--favicon.ico

|--loading-bars.svg

|--js

|--loading-bars.js

|--sb-admin-2.js

|--package.json

|--systemjs.config.js

|--tsconfig.json

|--typings.json

Lines 21-24 list the package definition and configuration files:

package.json: defines scripts and serves as documentation for what packages the Employee Tracker depends on.

systemjs.config.js: loads application and library modules.

tsconfig.json: is a configuration file to guide the TypeScript compiler as it generates JavaScript files.

Line 8 bootstraps the imported AppModule from app.module.ts file using the Just in Time (JIT) complier to launch the application.

Task 5. Create the root module

By convention, every Angular app has a root module class called AppModule in app.module.ts file. The @NgModule decorator allows us to bundle components, services, pipes, and directives at the module level. If you want to learn more about the benefits of using @NgModule, please check out Rob Wormald’s blog post.

We pass our three routes into the RouterModule.forRoot method which returns a module containing the configured Router. We export this module as the routing token.

NOTE: We registered routing with the root module named AppModule.

Task 7. Create our root component and its view

Add the root component named app.component.ts file to the app folder.

Replace the code in this file with the following:

JavaScript

1

2

3

4

5

6

7

8

import{Component}from'@angular/core';

@Component({

selector:'employee-tracker-app'

,templateUrl:'app/app.component.html'

})

exportclassAppComponent{}

The AppComponent is our application shell.

Add the view named app.component.html file to the app folder.

Replace the code in this file with the following:

XHTML

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

<div id="wrapper">

<!-- Navigation Area -->

<et-sidebar></et-sidebar>

<!-- End Navigation Area -->

<!-- Content Area -->

<div id="page-wrapper">

<!-- Routed View -->

<router-outlet></router-outlet>

</div>

<!-- End Content Area -->

</div>

The app.component.html file contains the master layout for our HTML. It provides a shell with two regions: a navigation area and a content area. The navigation area renders the sidebar view between the et-sidebar tags. The content area uses router-outlet directive to display the views produced by the Router. In other words, when you click a navigation link, it’s corresponding view is loaded in the content area.

Task 8. Create the Sidebar component and its view

Add the sidebar.component.ts file to the layout folder.

Replace the code in this file with the following:

JavaScript

1

2

3

4

5

6

7

8

9

10

import{Component}from'@angular/core';

@Component({

selector:'et-sidebar'

,templateUrl:'app/layout/sidebar.component.html'

})

exportclassSidebarComponent{

heading='Angular 2 Employee Tracker App';

}

The navigation area of the AppComponent uses et-sidebar to display the sidebar view.