Your privacy matters: This site uses cookies to analyze site usage and provide social media features. Learn More.

The following tutorial uses .NET Core 1.0.3 SDK Preview 2 build 3156 which can be downloaded from https://github.com/dotnet/core/blob/master/release-notes/download-archives/1.0.3-preview2-download.md. Future version of .NET Core may not work with this tutorial.

Angular 2 + .NET Core Tutorial Part III: Routing and Services

In the first two tutorials, an Angular 2 + .NET Core project was set up and coded. In this tutorial, the project will be expanded to use routing to display a single widget. In addition, a new service will be created to provide widget data to the components.

The application in this tutorial builds on the application completed in the previous tutorial. If you wish to complete part 2 of this tutorial, please go to https://www.accelebrate.com/library/tutorials/angular2-net-core-data-table and complete that tutorial, then come back here and use that application to complete this tutorial. If you wish to start this tutorial without completing the previous one, download the starter files for this tutorial from https://github.com/training4developers/dotnet-core-angular2-display-widgets

If you wish to download the completed application for this tutorial, please do so from
https://github.com/training4developers/dotnet-core-angular2-routing-services.

For the backend database, a local SQLite database file will be used. With a few slight modifications, the database could easily be switched to another platform (e.g., SQL Server or PostgreSQL) because the repository pattern and Entity Framework will be used by the REST services to retrieve data from the database.

Enhancing the REST Service to Get a Single Widget

First, a new endpoint for retrieving a single widget would need to be configured. To add this new endpoint, a new controller action for the Widgets controller should be coded. Open the file Widgets.cs in the Controllers folder and add the following method after the Get() method:

// specify parameters for the route
[HttpGet("{id}")]
public IActionResult Get(int id) {
var widget = _widgetRepo.Get(id);
// if no widget is found, return a 404 error
if (widget == null) {
return NotFound();
} return new ObjectResult(widget);
}

To provide the single widget data for the new REST endpoint, the Widget repository needs a new method that will allow the endpoint method to query the database for a single widget. First open the file named WidgetRepo.cs in the Data/Models folder. Then add the following method after the GetAll() method in the repository class:

public IWidget Get(int id) {
  // find a single widget, and ensure only a single widget is returned     
return _dbContext.Widgets.Where(w => w.Id == id).Select(w => new Widget {
Id = w.Id,
Name = w.Name,
Description = w.Description,
Color = w.Color,
Size = w.Size,
Quantity = w.Quantity,
Price = w.Price
}).SingleOrDefault();
}

The dependency injection system of .NET Core injects the repository into the controller for the widget’s REST service endpoint. To perform this injection, an interface is mapped to the concrete Widget repository class modified above. The Widgets controller references this interface. Using the interface map, the .NET Core dependency injector knows which service to inject. In order for the new method to be available, the IWidgetRepo interface must be updated to include the method in its definition.

First, open the file named IWidgetRepo.cs in the Interfaces folder. Then add the following method definition after the GetAll() definition:

IWidget Get(int id);

These changes complete the .NET Core modifications needed to support retrieving a single widget from the REST service.

To summarize the steps: add an action method to the controller, add a method to the repository class, and add the method definition to the interface class. The same steps will be needed in the next tutorial when we add extra endpoints for inserting, updating, and deleting widgets.

Create a Widgets Service

In the previous tutorial, the AppComponent component retrieved the widgets from the REST service by directly interacting with the Http service. While this is acceptable, usually such operations would be moved to a custom service and the custom service would be injected into the component. This allows for a cleaner separation of concerns and allow interactions with the data source to be abstracted from the perspective of the component. Unit testing will be easier since each part (the service and the component) can be tested independently.

To create a widgets data service, a folder should be created to keep the application files organized. In the wwwrootsrc/js/app folder, create a new folder named services. In the services folder create a new file named widgets.ts. Add the code directly below to the file. The code is also heavily commented in order to explain the purpose of each statement.

import { Injectable } from "@angular/core";
import { Http } from "@angular/http"; import { Observable } from "rxjs"; import { Widget } from "../models/widget"; // moved the widgets http code to a service so it can be utilized throughout
// the Angular 2 application // the Injectable decorator allows other services to be injected
// into this service, such as the Http service
@Injectable()
export class Widgets { // inject the Http service, and make it a private field
// on the service
constructor(private http: Http) { } // return all of the widgets
getAll(): Observable<Widget[]> {
return this.http.get('/widgets')
.map(res => res.json());
} // return a widget by id
get(widgetId: number): Observable<Widget> {
return this.http.get('/widgets/' + encodeURIComponent(widgetId.toString()))
.map(res => res.json());
}
}

With the service created, it now needs to be registered with the application module, AppModule, so that it can be injected into the various parts of the Angular 2 application.

To accomplish this, begin by opening the file named app.module.ts in the folder wwwrootsrc/js/app. After the import statement for the AppComponent, add the following lines of code:

// Imports the Widgets Service so it can be registered with the Application Module
import { Widgets } from "./services/widgets";

With the Widgets class now imported, the class should be registered as a service provider for the AppModule. Do this by adding the following lines of code to the NgModule decorator configuration right after the bootstrap property configuration:

// makes the service available to the component
providers: [ Widgets ],   

The new NgModule decorator configuration should look similar to this (the comments are not required):

// Decorates the class to be an Angular 2 module
// Each Angular 2 application has a top-level AppModule
// from which the application bootstraps itself
@NgModule({
// import the modules needed by AppModule
imports: [ BrowserModule, HttpModule ],
// Make the App components available for use in the templates
declarations: [ AppComponent ],
// Start the application from the App component
bootstrap: [ AppComponent ],
// makes the service available to the component
providers: [ Widgets ],
})
export class AppModule { }

With the service defined and registered, the widget table needs to be extracted to its own component.

Create a Widget Table Component

In the previous tutorial, the widget table was constructed directly inside the AppComponent component. For a very trivial, simple web application this may be appropriate. However, for almost all applications, it would be better to move the table to its own component and reference the component from the AppComponent. This approach has several advantages. First, from the perspective of the AppComponent, it separates the displaying of the table from the specific implementation of it. Secondly, the widget table could be reused in other applications if it’s not directly tied to the AppComponent of this application.

In order to move the widget table to its own component, some new folders will need to be created to keep the application files well organized.

In the folder wwwrootsrc/js/app, create a new folder named components. Next, in the new components folder, create a new folder named widget-table. In the new widget-table folder, three files will be created. The first file will be an empty SASS file for component-level styling. The second file will be a HTML file for the component template. Finally, a TypeScript file will be added which will contain the class for the component.

Begin by creating a new file named widget-table.component.scss in the wwwrootsrc/js/app/components/widget-table folder. This file will remain empty. Feel free to add any styling you desire there. Even when files such as this one remain empty, it is a good idea to provide the full complement of files for each component. This makes it easier to add new styling in the future. It also makes it clear where the component-level styling should be placed.

Create a new file named widget-table.component.html in the wwwrootsrc/js/app/components/widget-table folder. Add the following HTML markup to the file:

<table>
<thead>
<tr>
<th>Name</th>
<th>Color</th>
<th>Size</th>
<th>Quantity</th>
<th>Price</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let widget of widgets">
<td>{{widget.name}}</td>
<td>{{widget.color}}</td>
<td>{{widget.size}}</td>
<td>{{widget.quantity}}</td>
<td>{{widget.price}}</td>
<td>
<button type="button" (click)="viewWidget(widget.id)">View</button>
</td>
</tr>
</tbody>
</table>

Two modifications have been made to the template since the last tutorial. In the first modification, the widget description column has been removed and will be available only in the widget view component (which will be created in the next section). In the second modification, a new action column has been added along with a View button for each widget row. This button, when wired-up, will allow the user to view the details of a widget in a single widget view.

Finally, the WidgetTable class needs to be configured. First, create a new file name widget-table.component.ts in the wwwrootsrc/js/app/components/widget-table folder. Next, add the code directly below to the file. Please note, there are numerous code comments to explain each code statement.

import { Component, OnInit } from "@angular/core";
import { Router } from "@angular/router"; import { Widgets } from "../../services/widgets";
import { Widget } from "../../models/widget"; // The Component Decorator configures this class
// to serve as a component in the Angular 2 application
@Component({
// Used to identify the element the component will be applied to in
// the DOM structure of the web page
selector: "widget-table",
// Loads the component's specific styles
template: require("./widget-table.component.html"),
// Loads the component's template
styles: [ require("./widget-table.component.scss") ]
})
export class WidgetTable implements OnInit { // private field to hold an array of widgets
private widgets: Widget[] = []; constructor(
private widgetsSvc: Widgets, // provides the widgets data service
private router: Router // provides the router to navigate to other routes
) { } // retrieve all of the widgets from the REST service when
// the component is loaded
ngOnInit() {
this.widgetsSvc.getAll()
.subscribe(widgets => this.widgets = widgets);
} // navigate to the widget view component
viewWidget(widgetId: number) {
this.router.navigate(['widget', widgetId]);
} }

Like the previous tutorial, the ngOnInit life-cycle method is still used to load the widgets when the component has been initialized. A new method, viewWidget, has been added to trigger a view change to the single widget view using the router provided by the router module. The configuration of the router will be covered in a later section.

Lastly, the WidgetTable component needs to be registered with the AppModule.

Open the file named app.module.ts in the wwwrootsrc/js/app folder.

Add the following line of code after the import AppComponent statement:

import { WidgetTable } from "./components/widget-table/widget-table.component";

Next add the WidgetTable class to the NgModule declarations array by inserting the following line:

declarations: [ AppComponent, WidgetTable ],

This will make the WidgetTable component available in the module for use in templates and routing.

The single widget view will be configured in the next section.

Create a Single Widget View Component

In the previous section, the WidgetTable component was defined and registered with the AppModule. In this section, a similar process will be accomplished to add the WidgetView component.

In the folder wwwrootsrc/js/app/components, begin by creating a new folder named widget-view. In the widget-view folder, three new files will be created. The first file will be an empty SASS file for component-level styling. The second file will be a HTML file for the component template. In the last file a TypeScript file will be added containing the class for the component.

Create a new file named widget-view.component.scss in the wwwrootsrc/js/app/components/widget-view folder. This file will remain empty.

Create a new file named widget-view.component.html in the wwwrootsrc/js/app/components/widget-view folder. Add the following HTML markup to the file:

<div>Name: {{widget?.name}}</div>
<div>Description: {{widget?.description}}</div>
<div>Color: {{widget?.color}}</div>
<div>Size: {{widget?.size}}</div>
<div>Quantity: {{widget?.quantity}}</div>
<div>Price: {{widget?.price}}</div>
<button type="button" (click)="returnToList()">Return to List</button>

In the above template, observe the “widget?.” syntax. This is shorthand syntax for telling Angular 2 to only retrieve the name property if the widget is not null or undefined. This will prevent errors such as “name is not a property of undefined.” Additionally, there is a button for returning the user back to the widget table.

Finally, the WidgetView class needs to be configured. Create a new file name widget-view.component.ts in the wwwrootsrc/js/app/components/widget-view folder. Add the code directly below to the file. Included in the code are numerous code comments to explain each code statement.

import { Component, OnInit } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router"; import { Widgets } from "../../services/widgets";
import { Widget } from "../../models/widget";
@Component({
selector: "widget-view",
template: require("./widget-view.component.html"),
styles: [ require("./widget-view.component.scss") ]
})
export class WidgetView implements OnInit { // private field of the widget to view
public widget: Widget; constructor(
private widgets: Widgets, // the widgets data service
private route: ActivatedRoute, // the current route
private router: Router // the router to navigate to other routes
) { } // load the widget with the route param once the component
// has been loaded
public ngOnInit() {
this.route.params.subscribe(params =>
this.widgets.get(params["widgetId"])
.subscribe(widget => this.widget = widget));
} // navigate to the widget table
public returnToList() {
this.router.navigateByUrl("/");
} }

Observe in the ngOnInit method how the widget ids from the route parameters are retrieved. Angular 2’s router module uses observables to asynchronously process the route parameter information and makes it available once it has finished processing. In this view, two observables are chained together to first retrieve the widget id. Then the widget is retrieved from the REST service.

Finally the WidgetView component needs to be registered with the AppModule.

Open the file named app.module.ts in the wwwrootsrc/js/app folder.

Add the following line of code after the import WidgetTable statement:

import { WidgetView } from "./components/widget-view/widget-view.component";

Then add the WidgetView class to the NgModule declarations array using the following line:

declarations: [ AppComponent, WidgetTable, WidgetView ],

This will make the WidgetView component available in the module for use in templates and in routing.

In the next section the routing table and module are configured.

Client-Side Routing

The next step is to configure routing. Routing will permit views to be changed via the URL in the web browser. Through the URL the view will be selected. and if appropriate, the widget id will be retrieved. To configure routing, the Angular 2 Routing module is used. The routing module will be used to produce a custom module containing the routing map and configuration for the application.

To get started create a new file named app.router.ts in the wwwrootsrc/js/app folder. Next add the following source code to the file:

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

import { WidgetTable } from './components/widget-table/widget-table.component';
import { WidgetView } from './components/widget-view/widget-view.component'; // path is the url
// component is the component to load for the specified path
// :widgetId is the parameter
const appRoutes: Route[] = [
{ path: 'widget/:widgetId', component: WidgetView },
{ path: '', component: WidgetTable },
]; // construct a customized router module with the custom routes
// use the HTML5 History API instead of hashes for routing
export const AppRouterModule = RouterModule.forRoot(appRoutes, { useHash: false });
With the AppRouterModule now configured, it needs to be registered with the AppModule for the application.

Begin by opening the file named app.module.ts in the wwwrootsrc/js/app folder.

Add the following line of code after the import HttpModule statement:

import { AppRouterModule } from './app.router';

Next add the AppRouterModule class to the NgModule imports array with the following line:

imports: [ BrowserModule, FormsModule, HttpModule, AppRouterModule ],

This line will make the AppRouterModule module available in the module.

Updating AppComponent

The final step is to configure the AppComponent component to use the routing solution and to remove its code to download widgets.

Open the file named app.component.html in the wwwrootsrc/js/app folder. Now replace the code in the file with the following code:

<header>
<h1>Widget Application</h1>
</header>
<div>
<router-outlet></router-outlet>
</div>
<footer>
<small>{{copyright}}</small>
</footer>

The router-outlet component is where the views will be loaded for each route. The component defined on each route will be loaded into the router-outlet.

Next open the file named app.component.ts and replace the code in the file with the following code:

// imports the Component decorator from the core
// Angular 2 module
import { Component } from '@angular/core'; // The Component Decorator configures this class
// to serve as a component in the Angular 2 application
@Component({
// Used to identify the element the component will be applied to in
// the DOM structure of the web page
selector: 'main',
// Loads the component's specific styles
styles: [require('./app.component.scss')],
// Loads the component's template
template: require('./app.component.html'),
})
export class AppComponent { // use the current year for the copyright year
private copyright: string = `Copyright ${(new Date()).getFullYear()} Widget Application`;
}

Observe how all of the code referring to loading widgets, managing widget arrays, and even the Http service have been removed. In its place, is a simple copyright property which keeps the copyright up-to-date in the page footer. The template through the router-outlet now loads the different views based upon the current route. Now it is time to run the application.

Run the Application

You have done a lot of work to get this far. To run the application:

  1. Open a terminal, and change to the folder.
  2. From the terminal window, run the following command:
    $ npm start

  3. Open a web browser, and navigate to http://localhost:5000.

Next Steps

With Angular 2 routing now configured and a service coded to easily access widget REST service data, the next step is to add editing capabilities and output formatting to the application. In the next tutorial, a widget form with validation will be created and the operations insert, update, and delete will be wired-up. Finally, pipes will be used to format the data.

Author: Eric Greene, one of Accelebrate’s instructors

In-Depth Angular Training

For in-depth Angular training, click here to view all of
Accelebrate's Angular training courses for you and your staff.

 

Contact Us:

Accelebrate’s training classes are available for private groups of 3 or more people at your site or online anywhere worldwide.

Don't settle for a "one size fits all" public class! Have Accelebrate deliver exactly the training you want, privately at your site or online, for less than the cost of a public class.

For pricing and to learn more, please contact us.

Contact Us Train For Us

Toll-free in US/Canada:
877 849 1850
International:
+1 678 648 3113

Toll-free in US/Canada:
866 566 1228
International:
+1 404 420 2491

925B Peachtree Street, NE
PMB 378
Atlanta, GA 30309-3918
USA

Subscribe to our Newsletter:

Never miss the latest news and information from Accelebrate:

Microsoft Gold Partner

Please see our complete list of
Microsoft Official Courses

Recent Training Locations

Alabama

Huntsville

Montgomery

Birmingham

Alaska

Anchorage

Arizona

Phoenix

Tucson

Arkansas

Fayetteville

Little Rock

California

San Francisco

Oakland

San Jose

Orange County

Los Angeles

Sacramento

San Diego

Colorado

Denver

Boulder

Colorado Springs

Connecticut

Hartford

DC

Washington

Florida

Fort Lauderdale

Miami

Jacksonville

Orlando

Saint Petersburg

Tampa

Georgia

Atlanta

Augusta

Savannah

Idaho

Boise

Illinois

Chicago

Indiana

Indianapolis

Iowa

Ceder Rapids

Des Moines

Kansas

Wichita

Kentucky

Lexington

Louisville

Louisiana

Banton Rouge

New Orleans

Maine

Portland

Maryland

Annapolis

Baltimore

Hagerstown

Frederick

Massachusetts

Springfield

Boston

Cambridge

Michigan

Ann Arbor

Detroit

Grand Rapids

Minnesota

Saint Paul

Minneapolis

Mississippi

Jackson

Missouri

Kansas City

St. Louis

Nebraska

Lincoln

Omaha

Nevada

Reno

Las Vegas

New Jersey

Princeton

New Mexico

Albuquerque

New York

Buffalo

Albany

White Plains

New York City

North Carolina

Charlotte

Durham

Raleigh

Ohio

Canton

Akron

Cincinnati

Cleveland

Columbus

Dayton

Oklahoma

Tulsa

Oklahoma City

Oregon

Portland

Pennsylvania

Pittsburgh

Philadelphia

Rhode Island

Providence

South Carolina

Columbia

Charleston

Spartanburg

Greenville

Tennessee

Memphis

Nashville

Knoxville

Texas

Dallas

El Paso

Houston

San Antonio

Austin

Utah

Salt Lake City

Virginia

Richmond

Alexandria

Arlington

Washington

Tacoma

Seattle

West Virginia

Charleston

Wisconsin

Madison

Milwaukee

Alberta

Edmonton

Calgary

British Columbia

Vancouver

Nova Scotia

Halifax

Ontario

Ottawa

Toronto

Quebec

Montreal

Puerto Rico

San Juan

© 2013-2019 Accelebrate, Inc. All Rights Reserved. All trademarks are owned by their respective owners.