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 II: Display a Table of Data

In the first tutorial, an Angular 2 + .NET Core project was set up that, when executed, displayed Hello World in a web browser. In this tutorial, the project will be expanded to serve up a list of widgets from a .NET Core REST service. The data will be displayed in an Angular 2 component.

The application in this tutorial builds off the application completed in the previous tutorial. If you wish to complete part 1 of this tutorial, please visit https://www.accelebrate.com/library/tutorials/angular2-net-core) and complete the tutorial, then come back here and use that application as the starting point for completing this tutorial. If you wish to start this tutorial without completing the first one, download the starter files for this tutorial from:

https://github.com/training4developers/dotnet-core-angular2-starter

If you wish to download the completed application for this tutorial, then download it from:

https://github.com/training4developers/dotnet-core-angular2-display-widgets

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 (such as SQL Server or PostgreSQL), because the repository pattern and Entity Framework will be used by the REST services to retrieve data from the database. This tutorial is organized into four parts: SQLite configuration, Data Model implementation, REST service implementation, and Angular 2 component implementation.

Configuring SQLite

In the first tutorial, the NuGet packages for SQLite were added to the project.json file. If you wish to review the configuration, here are the configuration lines including the packages:

"Microsoft.EntityFrameworkCore.SQLite": "1.1.0",
"Microsoft.EntityFrameworkCore.Design": "1.1.0"

In addition to the above configuration, the connection string for the database was configured in the appsettings.json file:

"ConnectionStrings": {
  "DefaultConnection": "Filename=./widgets.db"
}

The connection string specifies the information needed by the application in order to connect to the database. For SQLite, this is merely a file path. When the application starts, if the database file does not exist, one will be created and populated with an initial set of data. To configure this population, the connection will need to be initialized and some initial population code with data will need to be added to the Startup.cs file.

Add the following namespace imports through using statements to the Startup.cs file. Add the following statements just below the current using statements:

using Microsoft.EntityFrameworkCore;

using Training4Developers.Interfaces; using Training4Developers.Data; using Training4Developers.Models; using DataModels = Training4Developers.Data.Models;

The editor will complain about missing types. These will be added throughout the tutorial.

To connect to the database, add the following code to the ConfigureServices method of the Startup class. Insert the following code before the AddMvc method call:

var connectionString = Configuration.GetConnectionString("DefaultConnection");

// SQLite DbContext
services.AddDbContext<ApplicationDbContext>(options =>
  options.UseSqlite(connectionString));

The GetConnectionString function retrieves the connection string information loaded from the appsettings.json file. The AddDbContext function configures the application’s database context to use SQLite and connect with the specified connection string.

To seed the database, add the following code as method to the Startup class.

public void SeedNewDatabase(IApplicationBuilder app) {

using (var context = app.ApplicationServices.GetRequiredService<ApplicationDbContext>()) {

if (context.Database.EnsureCreated()) {

context.AddRange( new DataModels.Widget { Name = "Red Small Widget", Description = "A red small widget.", Color = "red", Size = "small", Quantity = 4, Price = 3.45M },
new DataModels.Widget {
Name = "Blue Medium Widget", Description = "A blue medium widget.",
Color = "blue", Size = "medium", Quantity = 6, Price = 2.75M
},
new DataModels.Widget {
Name = "Orange Large Widget", Description = "A orange large widget.",
Color = "orange", Size = "large", Quantity = 10, Price = 13.65M
},
new DataModels.Widget {
Name = "Yellow Small Widget", Description = "A yellow small widget.",
Color = "yellow", Size = "small", Quantity = 3, Price = 9.55M
}
);

context.SaveChanges();
}
}
}

Add the following line of code to the top of the Configure method.

SeedNewDatabase(app);

This line of code will seed the database if needed.

Now that the configuration is completed, we now need to create the data model, repository, and application context.

Implementing the Data Model

In the main project folder, create a new folder name Data. Within the Data folder, create a new folder named Models.

The following folders should now be created:

<project_root>/Data
<project_root>/Data/Models

Two additional folders are needed: one to contain applications interfaces, and one for the application models. Create a new folder named Interfaces in the project root folder. Create another new folder named Models in the project root folder as well.

The following additional folders should now be created:

<project_root>/Interfaces
<project_root>/Models

Many .NET projects you may find online combine the application model classes with the data model classes. For small applications where their structure is similar, this can be an acceptable approach. However, for larger applications, the application model and the data models used to retrieve data are often structurally quite different. For this application, the application models will be coded as classes separate from the data model classes. Data model classes will be decorated with the needed attributes to properly create and use the SQLite database.

In the Data/Models folder, create a new file named Widget.cs. Copy and paste the following code into the file:

using System.ComponentModel.DataAnnotations.Schema;
namespace Training4Developers.Data.Models { [Table("widgets")] public class Widget { public int Id { get; set; } public string Name { get; set; } public string Description { get; set; } public string Color { get; set; } public string Size { get; set; } public int Quantity { get; set; } [Column(TypeName="Money")] public decimal Price { get; set; } } }

In the Data folder, create a new file named ApplicationDbContext.cs. Copy and paste the following code into the file:

using Microsoft.EntityFrameworkCore;
using Training4Developers.Data.Models;
namespace Training4Developers.Data { public class ApplicationDbContext : DbContext { public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { } public DbSet<Widget> Widgets { get; set; } } }

The Application Database Context will use the Data Model to create the database structure (if the file does not exist), and it will use the structure to retrieve widgets once the application is executing.

Data Model

With the Data Model and Application Database Context implemented, the repository and application model need to be created. The repository will use the database context to load data models that will populate application models. The repository returns the application models to the application. For this application, the MVC controllers will access the repositories to retrieve application models to return in the response to REST service requests. The repository and model classes implement interfaces to decouple the source of the application models from the MVC controller.

In the Interfaces folder, create a new file named IWidget.cs. Copy and paste the following code into the file:

namespace Training4Developers.Interfaces
{
  public interface IWidget
  {
    int Id { get; set; }
    string Name { get; set; }
    string Description { get; set; }
    string Color { get; set; }
    string Size { get; set; }
    int Quantity { get; set; }
    decimal Price { get; set; }
  }
}

The IWidget application model interface will allow different implementations of the widget application model to be used if desired. In general, coding to an interface results in application parts that are better designed and more easily tested, and that can be swapped out with different implementations at runtime via dependency injection.

In addition to the application model interface, the repository interface needs to be added.. Create a new file named IWidgetRepo.cs in the Interfaces folder. Copy and paste the following code into the file:

using System.Collections.Generic;

namespace Training4Developers.Interfaces { public interface IWidgetRepo { IEnumerable<IWidget> GetAll(); } }

For this tutorial, only the GetAll method is needed. Future parts of the tutorial will expand this interface and other classes to fully support all the standard REST service CRUD operations.

With the interfaces defined, the repository and application model classes need to be implemented.

In the Models folder, create a new file named Widget.cs. Copy and paste the following code into the file:

using Training4Developers.Interfaces;

namespace Training4Developers.Models { public class Widget : IWidget { public int Id { get; set; } public string Name { get; set; } public string Description { get; set; } public string Color { get; set; } public string Size { get; set; } public int Quantity { get; set; } public decimal Price { get; set; } } }

In the Data folder, create a new file named WidgetRepo.cs. Copy and paste the following code into the file:

using System.Collections.Generic;
using System.Linq;

using Training4Developers.Models;
using Training4Developers.Interfaces;

using WidgetData = Training4Developers.Data.Models.Widget;

namespace Training4Developers.Data
{
public class WidgetRepo: IWidgetRepo
{
private readonly ApplicationDbContext _dbContext;

public WidgetRepo(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
}

public IEnumerable<IWidget> GetAll() {
return _dbContext.Widgets.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
});
}
}
}

The .NET Core dependency injection system will inject the Application DB Context when instantiating a new widget repository. The dependency injection system knows how to find the desired application database context class because of the configuration code from the SQLite configuration section of this tutorial:

// SQLite DbContext
services.AddDbContext<ApplicationDbContext>(options =>
  options.UseSqlite(connectionString));

Providing a built-in dependency injection system is one of the many useful features of .NET Core. With the repository class coded, the repository class needs to be registered with the dependency injection so it can be injected into the MVC controllers in the third part of this tutorial.

In the Startup.cs file in the main project folder, add the following code at the end of the ConfigureServices method:

services.AddScoped<IWidgetRepo, WidgetRepo>();

When adding classes for dependency injection, three methods can be used:

  • AddTransient
  • AddScoped
  • AddSingleton

Each method returns a service with a different life span. A transient service is instantiated on each injection. A scoped service is instantiated once for each request, but within a request is reused on each subsequent injection. A singleton service is created once within the lifetime of the application, and reused for all injections for all requests. The scoped lifetime is the most appropriate for the repository class in this application.

With the data model, application model, repository and database context configured, it’s time to complete the implementation of the REST service.

Implementing the REST Service

REST services have become the fundamental building block of data services for web applications. Particularly for single-page applications, such as Angular 2 applications, REST services provide the data needed to power the user interface. REST services using JSON have become popular because the interface makes development easy and JSON is readily understood by JavaScript in the web browser. Newer technologies such as GraphQL are emerging, but for now, REST services are the king of data access for HTTP-based applications.

Implementing the REST services requires the addition of a controller class that will handle the requests for widgets.

In the Controllers folder, create a new file named Widgets.cs. Copy and paste the following code into the file:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;

using Training4Developers.Interfaces;
using Training4Developers.Models;

namespace Training4Developers.Controllers
{
[Route("[controller]")]
public class Widgets : Controller
{
private readonly IWidgetRepo _widgetRepo;

public Widgets(IWidgetRepo widgetRepo)
{
_widgetRepo = widgetRepo;
}

[HttpGet]
public IActionResult Get()
{
return new ObjectResult(_widgetRepo.GetAll());
}
}
}

When consumers of the REST service make requests, the ASP.NET MVC framework parses the path of the URL to determine which controller will handle the request. Generally, selecting the controller and action follow certain conventions to minimize configuration, but when configuration is needed, it can be applied in two places: directly on the controller and action methods through Route attributes and in the Startup class with the UseMvc method to configure routes using a lambda function.

This project will use both approaches. The routes configured in the Startup class are the default route and a single-page application route. The default route performs the default mapping of a standard URL path to a controller class and action method. The single-page application route permits the use of History API client-side routing. Client-side routing allows all URLs that do not correspond to server resources to return the default home index page; this will be covered in the next tutorial.

Route attributes are used to decorate controller classes and action methods with additional routing information, such as mapping the controller or action to a different path. Route attributes do have downsides, though, as they unnecessarily couple the controller to the router. Typically, this is a non-issue as ASP.NET MVC controllers are only ever used with the ASP.NET MVC router; nevertheless, it’s worth mentioning there is nothing about a controller in general that requires a tightly coupled URL router.

The Route attribute on the controller class indicates this class will fulfill the controller part of the route configuration. There is another special attribute named HttpGet on the Get method. This attribute indicates this action method will only handle GET requests.

The constructor parameters are configured to receive the Widgets repository. When a request is received, ASP.NET MVC instantiates a new controller object. The controller object is not instantiated directly by the application’s code. Therefore, ASP.NET MVC needs to know where to get the constructor parameter from. This is accomplished through the dependency injection mechanism provided by ASP.NET Core. In the last section, the injection for the Widgets repository was configure in the Startup class.

Finally, the Get action method requests and returns the array from Widgets from the repository.

With the MVC controller implemented, an initial working REST service has been completed. At this point, you can run the project (refer to the first tutorial to see a full description of how to run the project). An easy way to run the project is to open a terminal window, change to the project folder, and run the following command:

$ npm start

Note: If you downloaded the completed version of the first tutorial from GitHub, npm start may fail because the NPM packages have not been installed. First, install the packages, then start the project.

$ npm install
$ npm start

Once the web server is running, navigate with a web browser to http://localhost:5000/widgets to see the list of widgets. The server portion of this tutorial is completed. In the next section, the Angular 2 AppComponent component will be coded to display the list in a table.

Displaying Data with an Angular 2 Component

Displaying the data in the Angular 2 AppComponent component will require the Angular 2 HTTP Module to be loaded in the AppModule. Then the Http service can be injected into the AppComponent. Then the widget data can be requested via the HTTP service and displayed.

First, add the following code to the app.module.ts file, right after the import BrowserModule line:

import { HttpModule } from '@angular/http';

Once the Http module is loaded, it must be added to the AppModule configuration.

Modify the imports configuration in the NgModule decorator to include the imported Http module.

imports: [ BrowserModule, HttpModule ],

Next, the AppComponent component needs to be updated to request the widgets and display them.

First, import the Http service class into the file named app.component.ts. Add the import statement directly after the import Component statement.

import { Http } from '@angular/http';

Second, add a constructor to the AppComponent class. The Http service will be injected into the component, and made available as a private property.

constructor(private http: Http) { }

To load the widgets when the AppComponent instance is initialized, the OnInit interface needs to be imported and applied to the AppComponent class.

To import the OnInit interface, update the import Component line to look like this:

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

Next, apply the interface to the class, by modifying the class declaration code as such:

export class AppComponent implements OnInit {

Finally, to implement the interface add the following function to the class:

ngOnInit() {
  this.http.get('/widgets')
    .map(res => res.json())
    .subscribe(widgets => {
      this.widgets = widgets;
    });
}

The AppComponent is almost done. A widgets property should be added and the component template needs to be updated.

To configure the widgets property on the component, a Widget interface will need to be created.

In the <project_root>/wwwrootsrc/js/app folder, create a new folder named models. In the folder, create a new file named widget.ts. Copy and paste the following code into the file:

export interface Widget {
  id: number;
  name: string;
  description: string;
  color: string;
  size: string;
  quantity: number;
  price: number;
}

The AppComponent will need to be imported in order use the interface. Add the following import statement after the last import statement in the file:

import { Widget } from './models/widget';

Using the Widget interface, configure the widgets property on the AppComponent class with the following code.

widgets: Widget[] = null;

The final step is update the app.component.html file with a new template that displays a table of widgets. Add the following code to the template file.

<h1>Widgets Tool</h1>
<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Description</th>
      <th>Color</th>
      <th>Size</th>
      <th>Quantity</th>
      <th>Price</th>
    </tr>
  </thead>
  <tbody>
   <tr *ngFor="let widget of widgets">
     <td>{{widget.name}}</td>
     <td>{{widget.description}}</td>
     <td>{{widget.color}}</td>
     <td>{{widget.size}}</td>
     <td>{{widget.quantity}}</td>
     <td>{{widget.price}}</td>
    </tr>
   </tbody>
</table>

The AppComponent will now request widgets from the REST service, populate the widgets property and display the widgets with the updated template.

Open a web browser, and navigate to http://localhost:5000 to see the table of widgets.

Next Steps

With the completion of the second part of the tutorial series, database and REST services have been partially implemented, and Angular 2, through the Http Service, is displaying data using an Angular 2 component. In the next tutorial, the emphasis will be on Angular 2, configuring routing, wrapping the REST service requests in a custom service, and organizing client-side files for a larger application.

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.