Creating SPA Using ASP.NET Core 2.0 And Angular v6

In this post, we are going to explore how a Single Page Application (SPA) sample can be put together using ASP.Net Core & Angular from scratch.

Table of Contents

  • Introduction
  • Work Plan
  • Development Environment
  • Core Description
  • Summary

Introduction

We are going to use Angular6, TypeScript in the frontend and in the backend we'll be using ASP.NET Core WebAPI with Entity Framework Core database operations.

ASP.NET Core

Work Plan

  • Create New ASP.Net Core Project
  • Configure Newly Created Project
    • Serve Static Page
  • Install Node Packages
  • Manage Installed Packages
  • Create Frontend Application
    • HTML Templating
    • Folder Structure
    • Angular Dependencies
    • TypeScript Configuration
    • Root Component
    • Root Module
    • Bootstrapping Client app
  • Creating Database
  • Install Entity Framework Core
  • Scaffolding MSSQL Database
  • Configure Middleware
  • Creating ASP.Net Core WebAPI
  • Creating Client Services
  • Perform CRUD Operation
  • Test in Browser

Development Environment

Following are the prerequisites to develop our SPA sample.

  • Visual Studio 2017
  • NET Core 2.0 or later
  • NodeJS and NPM

Visual Studio 2017

If you already have a copy of Visual Studio 2017 installed don’t worry, otherwise download Visual Studio Community 2017 for free.

.NET Core Downloads

Install the .NET Core SDK (Software Development Kit) 2.0 or later.

NodeJS and NPM

Install  the latest NodeJS and NPM

Make sure the environment is ready before stepping into this.

Create ASP.Net Core Project

Let’s create a new project with Visual Studio 2017 > File > New > Project.

ASP.NET Core

 

Choose empty template click > OK.

ASP.NET Core

Here’s our new ASP.Net Core empty project with initial components.

ASP.NET Core

Configure ASP.Net Core Project

In this sample, we are going to serve the index.html page as our main page from app root folder, to serve the page we need to configure in Startup.cs file. Here is the code snippet which is going to serve the page.

  1. DefaultFilesOptions options = new DefaultFilesOptions();  
  2. options.DefaultFileNames.Clear();  
  3. options.DefaultFileNames.Add("/index.html");  
  4. app.UseDefaultFiles(options);  
  5. app.UseStaticFiles();  

Middleware to Handle Client Side Routes Fallback

To avoid 404 error while reloading the page in AngularJS SPA app we need to add middleware to handle client side route fallback. The below code snippet will take care of that. Here’s the original post: https://code.msdn.microsoft.com/How-to-fix-the-routing-225ac90f

  1. app.Use(async (context, next) =>  
  2. {  
  3.     await next();  
  4.     if (context.Response.StatusCode == 404 && !Path.HasExtension(context.Request.Path.Value))  
  5.     {  
  6.         context.Request.Path = "/index.html";  
  7.         context.Response.StatusCode = 200;  
  8.         await next();  
  9.     }  
  10. });  

Get more details on middleware here: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?tabs=aspnetcore2x

Install Node Packages

Let’s add frontend packages to our application. We need to add npm configuration file named package.json.

To do that right click the project then Goto > Add > New Item. From the new item add window choose npm Configuration File.

Here’s our list of frontend package dependencies.

  1. {  
  2.   "version""1.0.0",  
  3.   "name""asp.net",  
  4.   "private"true,  
  5.   "dependencies": {  
  6.     "@angular/common""^6.0.2",  
  7.     "@angular/compiler""^6.0.2",  
  8.     "@angular/core""^6.0.2",  
  9.     "@angular/forms""^6.0.2",  
  10.     "@angular/http""^6.0.2",  
  11.     "@angular/platform-browser""^6.0.2",  
  12.     "@angular/platform-browser-dynamic""^6.0.2",  
  13.     "@angular/router""^6.0.2",  
  14.     "@angular/upgrade""^6.0.2",  
  15.     "bootstrap""^4.1.1",  
  16.     "core-js""^2.5.6",  
  17.     "reflect-metadata""^0.1.12",  
  18.     "rxjs""^6.1.0",  
  19.     "systemjs""^0.21.3",  
  20.     "zone.js""^0.8.26"  
  21.   },  
  22.   "devDependencies": {  
  23.     "@types/core-js""^0.9.46",  
  24.     "typescript""^2.8.3",  
  25.     "typings""^2.1.1",  
  26.     "@types/node""^10.0.4",  
  27.     "concurrently""^3.5.1",  
  28.     "json-server""^0.12.2",  
  29.     "gulp""^3.9.1",  
  30.     "gulp-concat""^2.6.1",  
  31.     "gulp-rename""^1.2.2",  
  32.     "gulp-cssmin""^0.2.0",  
  33.     "gulp-uglify""^3.0.0",  
  34.     "gulp-htmlclean""^2.7.20",  
  35.     "rimraf""^2.6.2"  
  36.   }  
  37. }   

After installation of all packages let’s transfer the required libraries from node_modules folder to “wwwroot/lib” for calling it to the main HTML page.

Manage Installed Packages

We need to add a task runner like gulp file, then copy below code snippet and paste it into the newly added file.

  1. /// <binding AfterBuild='build-all' />  
  2.   
  3. var gulp = require("gulp"),  
  4.     rimraf = require("rimraf"),  
  5.     concat = require("gulp-concat"),  
  6.     cssmin = require("gulp-cssmin"),  
  7.     uglify = require("gulp-uglify"),  
  8.     rename = require("gulp-rename");  
  9.   
  10. var root_path = {  
  11.     webroot: "./wwwroot/"  
  12. };  
  13.   
  14. //library source  
  15. root_path.nmSrc = "./node_modules/";  
  16.   
  17. //library destination  
  18. root_path.package_lib = root_path.webroot + "lib/";  
  19.   
  20. gulp.task('copy-lib-js'function () {  
  21.   
  22.     gulp.src('./node_modules/core-js/**/*.js')  
  23.         .pipe(gulp.dest(root_path.package_lib + 'core-js'));  
  24.     gulp.src('./node_modules/@angular/**/*.js')  
  25.         .pipe(gulp.dest(root_path.package_lib + '@angular'));  
  26.     gulp.src('./node_modules/zone.js/**/*.js')  
  27.         .pipe(gulp.dest(root_path.package_lib + 'zone.js'));  
  28.     gulp.src('./node_modules/systemjs/**/*.js')  
  29.         .pipe(gulp.dest(root_path.package_lib + 'systemjs'));  
  30.     gulp.src('./node_modules/reflect-metadata/**/*.js')  
  31.         .pipe(gulp.dest(root_path.package_lib + 'reflect-metadata'));  
  32.     gulp.src('./node_modules/rxjs/**/*.js')  
  33.         .pipe(gulp.dest(root_path.package_lib + 'rxjs'));  
  34. });  
  35.   
  36. gulp.task("copy-all", ["copy-lib-js"]);  
  37. //Copy End  
  38.   
  39. gulp.task('min-js'function () {  
  40.     gulp.src(['./clientapp/**/*.js'])  
  41.         .pipe(uglify())  
  42.         .pipe(gulp.dest(root_path.webroot + 'app'))  
  43. });  
  44.   
  45. gulp.task('copy-html'function () {  
  46.     gulp.src('clientapp/**/*.html')  
  47.         .pipe(gulp.dest(root_path.webroot + 'app'));  
  48. });  
  49.   
  50. gulp.task("build-all", ["min-js""copy-html"]);  
  51. //Build End  

Right click on gulpfile.js then go to “Task Runner Explorer”.

ASP.NET Core

From the new window, refresh the task, then right click on the task to run it like the below screen.

ASP.NET Core

As we can see our required libraries are loaded in “wwwroot/lib” folder.

ASP.NET Core

Frontend Application

HTML Templating

In this section, we are going to add a basic Bootstrap template to our main HTML page.

ASP.NET Core

Folder Structure

Below is our folder structure for the sample app. As we can see 'clientapp' is the root folder that consists of entry point file & root module. Root module has app component where other components are related to root module through app component.

ASP.NET Core

Angular Dependencies

To enable an ES6 new feature we need to resolve dependencies.

SystemJS Import

Here’s our application loading to the browser by System.import() function.

  1. System.import('app/main.js').catch(function (err) { console.error(err); });  

Before importing module we need to configure SystemJS by using System.config() function we are going to define which package/file to load.

SystemJS Config - systemjs.config.js

  1. /** 
  2.  * System configuration for Angular samples 
  3.  * Adjust as necessary for your application needs. 
  4.  */  
  5. (function (global) {  
  6.     System.config({  
  7.         paths: {  
  8.             // paths serve as alias  
  9.             'npm:''/lib/'  
  10.         },  
  11.         // map tells the System loader where to look for things  
  12.         map: {  
  13.             // our app is within the app folder  
  14.             'app''app',  
  15.   
  16.             // angular bundles  
  17.             '@angular/core''npm:@angular/core/bundles/core.umd.js',  
  18.             '@angular/common''npm:@angular/common/bundles/common.umd.js',  
  19.             '@angular/compiler''npm:@angular/compiler/bundles/compiler.umd.js',  
  20.             '@angular/platform-browser''npm:@angular/platform-browser/bundles/platform-browser.umd.js',  
  21.             '@angular/platform-browser-dynamic''npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',  
  22.             '@angular/http''npm:@angular/http/bundles/http.umd.js',  
  23.             '@angular/router''npm:@angular/router/bundles/router.umd.js',  
  24.             '@angular/forms''npm:@angular/forms/bundles/forms.umd.js',  
  25.   
  26.             // other libraries  
  27.             'rxjs''npm:rxjs',  
  28.             'rxjs-compat''npm:rxjs-compat',  
  29.             'rxjs/operators''npm:rxjs/operators'  
  30.         },  
  31.         // packages tells the System loader how to load when no filename and/or no extension  
  32.         packages: {  
  33.             'app': {  
  34.                 main: 'main.js',  
  35.                 defaultExtension: 'js',  
  36.                 meta: {  
  37.                     '': {  
  38.                         format: 'cjs'  
  39.                     }  
  40.                 }  
  41.             },  
  42.             'rxjs': {  
  43.                 main: 'index.js',  
  44.                 defaultExtension: 'js'  
  45.             },  
  46.             'rxjs/operators': {  
  47.                 main: 'index.js',  
  48.                 defaultExtension: 'js'  
  49.             }  
  50.         }  
  51.     });  
  52. })(this);  

TypeScript Configuration

We need to convert our typescript code to JavaScript for browser support by configuring TypeScript compiler. The below code snippet is for tsconfig.json file.

Configure Typescript - tsconfig.json

  1. {  
  2.   "compileOnSave"false,  
  3.   "compilerOptions": {  
  4.     "baseUrl""./",  
  5.     "sourceMap"true,  
  6.     "declaration"false,  
  7.     "moduleResolution""node",  
  8.     "emitDecoratorMetadata"true,  
  9.     "experimentalDecorators"true,  
  10.     "target""es5",  
  11.     "typeRoots": [  
  12.       "node_modules/@types"  
  13.     ],  
  14.     "lib": [  
  15.       "es2017",  
  16.       "dom"  
  17.     ],  
  18.     "types": [  
  19.       "core-js"  
  20.     ]  
  21.   },  
  22.   "includes": [  
  23.     "/**/*.ts"  
  24.   ]  
  25. }  

Root Component

Every application has a root component which has two part annotation and definition. It has a selector to match with main HTML page to render.

  1. import { Component } from '@angular/core';  
  2. @Component({  
  3.     selector: 'my-app',  
  4.     templateUrl: './app/component/app/app.html'  
  5. })  
  6. export class AppComponent {}  

 ASP.NET Core

Root Module

This is where our application components are defined, a module may have multiple components. Every application has a root module.

  1. import { NgModule } from '@angular/core';  
  2. import { BrowserModule } from '@angular/platform-browser';  
  3. import { Routes, RouterModule } from '@angular/router';  
  4. import { LocationStrategy, HashLocationStrategy } from '@angular/common';  
  5. import { FormsModule, ReactiveFormsModule } from '@angular/forms';  
  6.   
  7. //Components  
  8. import { AppComponent } from './component/app/component';  
  9. import { HomeComponent } from './component/home/component';  
  10. import { AboutComponent } from './component/about/component';  
  11. import { UserComponent } from './component/user/component';  
  12.   
  13. //Routes  
  14. const routes: Routes = [  
  15.     { path: '', redirectTo: 'home', pathMatch: 'full' },  
  16.     { path: 'home', component: HomeComponent },  
  17.     { path: 'about', component: AboutComponent },  
  18.     { path: 'user', component: UserComponent }  
  19. ];  
  20.   
  21. @NgModule({  
  22.     declarations: [AppComponent, HomeComponent, AboutComponent, UserComponent],  
  23.     imports: [BrowserModule, FormsModule, ReactiveFormsModule, RouterModule.forRoot(routes)],  
  24.     bootstrap: [AppComponent]  
  25. })  
  26.   
  27. export class AppModule { }  

Bootstrapping Clientapp

Let’s create a main entry file, name it main.ts. Copy below code snippet paste it to newly created typescript file.

  1. import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';  
  2. import { AppModule } from './module';  
  3. const platform = platformBrowserDynamic();  
  4. platform.bootstrapModule(AppModule);  

At this point our application is bootstrapped and launch the root module in browser. Here's how we are bootstrapping our application in index.html page.

Main Html Page

  1. <my-app>  
  2.     <img src="img/ajax_small.gif" />  Please wait ...  
  3. </my-app>  
  4. <!-- Core JS Files   -->  
  5. <script src="/js/jquery/jquery-1.10.2.js" type="text/javascript"></script>  
  6. <script src="/js/bootstrap/tether.min.js"></script>  
  7. <script src="/js/bootstrap/bootstrap.min.js" type="text/javascript"></script>  
  8.   
  9. <!-- App JS Files   -->  
  10. <!-- load the dependencies -->  
  11. <script src="lib/core-js/client/shim.js" type="text/javascript"></script>  
  12. <script src="lib/zone.js/dist/zone.js" type="text/javascript"></script>  
  13. <script src="lib/reflect-metadata/Reflect.js" type="text/javascript"></script>  
  14.   
  15. <!-- load our angular app with systemjs -->  
  16. <script src="lib/systemjs/dist/system.src.js" type="text/javascript"></script>  
  17. <script src="systemjs.config.js" type="text/javascript"></script>  
  18.   
  19. <script type="text/javascript">  
  20.     System.import('app/main.js').catch(function (err) { console.error(err); });  
  21. </script>  

Let’s build and run the application.

ASP.NET Core

Our application is running, as we can see the upper screen is appearing with a welcome message. Next, we will create form then submit & validate after that we are going to perform CRUD operations with SQL Database.

Creating Database

Let’s Create a Database in MSSQL Server. Here is the table where we are storing data. Run the below script in a query window to create a new database.

CREATE DATABASE [dbCore]

Creating Table

  1. USE [dbCore]  
  2. GO  
  3.   
  4. SET ANSI_NULLS ON  
  5. GO  
  6.   
  7. SET QUOTED_IDENTIFIER ON  
  8. GO  
  9.   
  10. CREATE TABLE [dbo].[User](  
  11.     [Id] [int] IDENTITY(1,1) NOT NULL,  
  12.     [FirstName] [nvarchar](250) NULL,  
  13.     [LastName] [nvarchar](250) NULL,  
  14.     [Email] [nvarchar](250) NULL,  
  15.     [Phone] [nvarchar](50) NULL,  
  16.  CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED   
  17. (  
  18.     [Id] ASC  
  19. )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ONON [PRIMARY]  
  20. ON [PRIMARY]  
  21. GO  

Install Entity Framework Core

Entity Framework (EF) Core is data access technology which is targeted for cross-platform. Let’s right click on project then GoTo > Tools > NuGet Package Manager > Package Manager Console install below packages one by one.

  • Install-Package Microsoft.EntityFrameworkCore.SqlServer
  • Install-Package Microsoft.EntityFrameworkCore.SqlServer.Design
  • Install-Package Microsoft.EntityFrameworkCore.Tools.DotNet

EntityFrameworkCore.SqlServer

Database Provider, that allows Entity Framework Core to be used with Microsoft SQL Server.

EntityFrameworkCore.SqlServer.Design

Design-time, that allows Entity Framework Core functionality (EF Core Migration) to be used with Microsoft SQL Server.

EntityFrameworkCore.Tools

Command line tool for EF Core that Includes Commands 

Scaffolding MSSQL Database

We are going to generate EF models from existing database using reverse engineering using a command in Package Manager Console.

ASP.NET Core

Command

Scaffold-DbContext "Server=DESKTOP-7OJNKVF;Database=dbCore;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -Output serverapp/models

For Package Manager Console:

  • Scaffold-DbContext
  • Add-Migration
  • Update-Database

For Command Window

  • dotnet ef dbcontext scaffold

As we can see from solution explorer models folder is created with Context & Entities.

ASP.NET Core

Now, open the DbContext file then add a constructor to pass configuration like connectionstring into the DbContext.

  1. public dbCoreContext(DbContextOptions<dbCoreContext> options) : base(options)  
  2. {  
  3. }  
  4.   
  5. protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)  
  6. {  
  7.     //if (!optionsBuilder.IsConfigured)  
  8.     //{  
  9.     //    #warning To protect potentially sensitive information in your connection string, you should move it out of source code. See http://go.microsoft.com/fwlink/?LinkId=723263 for guidance on storing connection strings.  
  10.     //    optionsBuilder.UseSqlServer(@"Server=DESKTOP-7OJNKVF;Database=dbCore;Trusted_Connection=True;");  
  11.     //}  
  12. }  

Configure Middleware

Register DbContext

In Startup.cs let’s add our DbContext as service to enable database connection.

  1. //Database Connection  
  2. var connection = @"Server=DESKTOP-7OJNKVF;Database=dbCore;Trusted_Connection=True;";  
  3. services.AddDbContext<dbCoreContext>(options => options.UseSqlServer(connection));  

Creating ASP.Net Core WebAPI

Here’s our ASP.Net Core WebAPI Controller using specific RoutePrefix attribute globally. With this api controller class we are performing a database operation using Entity Framework DbContext.

  1. [Route("api/Values"), Produces("application/json"), EnableCors("AppPolicy")]  
  2. public class ValuesController : Controller  
  3. {  
  4.     private dbCoreContext _ctx = null;  
  5.     public ValuesController(dbCoreContext context)  
  6.     {  
  7.         _ctx = context;  
  8.     }  
  9.   
  10.   
  11.     // GET: api/Values/GetUser  
  12.     [HttpGet, Route("GetUser")]  
  13.     public async Task<object> GetUser()  
  14.     {  
  15.         List<User> users = null;  
  16.         object result = null;  
  17.         try  
  18.         {  
  19.             using (_ctx)  
  20.             {  
  21.                 users = await _ctx.User.ToListAsync();  
  22.                 result = new  
  23.                 {  
  24.                     User  
  25.                 };  
  26.             }  
  27.         }  
  28.         catch (Exception ex)  
  29.         {  
  30.             ex.ToString();  
  31.         }  
  32.         return users;  
  33.     }  
  34.   
  35.     // GET api/Values/GetByID/5  
  36.     [HttpGet, Route("GetByID/{id}")]  
  37.     public async Task<User> GetByID(int id)  
  38.     {  
  39.         User user = null;  
  40.         try  
  41.         {  
  42.             using (_ctx)  
  43.             {  
  44.                 user = await _ctx.User.FirstOrDefaultAsync(x => x.Id == id);  
  45.             }  
  46.         }  
  47.         catch (Exception ex)  
  48.         {  
  49.             ex.ToString();  
  50.         }  
  51.         return user;  
  52.     }  
  53.   
  54.   
  55.     // POST api/Values/Save  
  56.     [HttpPost, Route("Save")]  
  57.     public async Task<object> Save([FromBody]vmUser model)  
  58.     {  
  59.         object result = null; string message = "";  
  60.         if (model == null)  
  61.         {  
  62.             return BadRequest();  
  63.         }  
  64.         using (_ctx)  
  65.         {  
  66.             using (var _ctxTransaction = _ctx.Database.BeginTransaction())  
  67.             {  
  68.                 try  
  69.                 {  
  70.                     if (model.id > 0)  
  71.                     {  
  72.                         var entityUpdate = _ctx.User.FirstOrDefault(x => x.Id == model.id);  
  73.                         if (entityUpdate != null)  
  74.                         {  
  75.                             entityUpdate.FirstName = model.firstName;  
  76.                             entityUpdate.LastName = model.lastName;  
  77.                             entityUpdate.Phone = model.phone;  
  78.                             entityUpdate.Email = model.email;  
  79.                             await _ctx.SaveChangesAsync();  
  80.                         }  
  81.                     }  
  82.                     else  
  83.                     {  
  84.                         var UserModel = new User  
  85.                         {  
  86.                             FirstName = model.firstName,  
  87.                             LastName = model.lastName,  
  88.                             Email = model.email,  
  89.                             Phone = model.phone  
  90.                         };  
  91.   
  92.                         _ctx.User.Add(UserModel);  
  93.                         await _ctx.SaveChangesAsync();  
  94.                     }  
  95.   
  96.                     _ctxTransaction.Commit();  
  97.                     message = "Saved Successfully";  
  98.                 }  
  99.                 catch (Exception e)  
  100.                 {  
  101.                     _ctxTransaction.Rollback();  
  102.                     e.ToString();  
  103.                     message = "Saved Error";  
  104.                 }  
  105.   
  106.                 result = new  
  107.                 {  
  108.                     message  
  109.                 };  
  110.             }  
  111.         }  
  112.         return result;  
  113.     }  
  114.   
  115.     // DELETE api/Values/DeleteByID/5  
  116.     [HttpDelete, Route("DeleteByID/{id}")]  
  117.     public async Task<object> DeleteByID(int id)  
  118.     {  
  119.         object result = null; string message = "";  
  120.         using (_ctx)  
  121.         {  
  122.             using (var _ctxTransaction = _ctx.Database.BeginTransaction())  
  123.             {  
  124.                 try  
  125.                 {  
  126.                     var idToRemove = _ctx.User.SingleOrDefault(x => x.Id == id);  
  127.                     if (idToRemove != null)  
  128.                     {  
  129.                         _ctx.User.Remove(idToRemove);  
  130.                         await _ctx.SaveChangesAsync();  
  131.                     }  
  132.                     _ctxTransaction.Commit();  
  133.                     message = "Deleted Successfully";  
  134.                 }  
  135.                 catch (Exception e)  
  136.                 {  
  137.                     _ctxTransaction.Rollback(); e.ToString();  
  138.                     message = "Error on Deleting!!";  
  139.                 }  
  140.   
  141.                 result = new  
  142.                 {  
  143.                     message  
  144.                 };  
  145.             }  
  146.         }  
  147.         return result;  
  148.     }  
  149. }  

All right, our WebAPI is ready to interact with the database. Our next step is to prepare client model, component and services to interact with WebAPI’s.

Perform CRUD Operation

Let’s get started with form design. There is two strategy of Angular6 form, in this sample, we have used Model-driven form.

  1. Template-driven
  2. Model-driven

UI: user.html

  1. <div class="container-fluid">  
  2.     <div class="row">  
  3.         <div class="col-sm-4">  
  4.             <h3>User Details</h3>  
  5.             <form [formGroup]="userForm" #f="ngForm" (ngSubmit)="save()">  
  6.                 <div class="form-group">  
  7.                     <label for="firstName">First Name</label><em *ngIf="userForm.controls['firstName'].hasError('required')" class="text-danger">*</em>  
  8.                     <input type="text" id="firstName" name="firstName" formControlName="firstName" class="form-control" placeholder="First Name" required />  
  9.                 </div>  
  10.                 <div class="form-group">  
  11.                     <label for="lastName">Last Name</label><em *ngIf="userForm.controls['lastName'].hasError('required')" class="text-danger">*</em>  
  12.                     <input type="text" formControlName="lastName" class="form-control" placeholder="Last Name" required />  
  13.                 </div>  
  14.                 <div class="form-group">  
  15.                     <label for="email">Email</label><em *ngIf="userForm.controls['email'].hasError('required')" class="text-danger">*</em>  
  16.                     <input type="email" formControlName="email" class="form-control" placeholder="Email" required />  
  17.                     <span *ngIf="userForm.controls['email'].hasError('pattern')" class="text-danger">  
  18.                         Invalid Email   
  19.                     </span>  
  20.                 </div>  
  21.                 <div class="form-group">  
  22.                     <label for="phone">Phone</label><em *ngIf="userForm.controls['phone'].hasError('required')" class="text-danger">*</em>  
  23.                     <input type="text" formControlName="phone" class="form-control" placeholder="Phone" required />  
  24.                 </div>  
  25.                 <div class="form-group">  
  26.                     <button type="button" class="btn btn-danger" (click)="reset()">Reset</button>  
  27.                     <button type="submit" class="btn btn-primary ml-10" [disabled]="!f.valid">Save</button>  
  28.                 </div>  
  29.             </form>  
  30.             <span class="warning"></span>  
  31.         </div>  
  32.         <div class="col-sm-8">  
  33.             <h3>All User</h3>  
  34.             <table style="width:100%" class="table table-striped">  
  35.                 <tr>  
  36.                     <th>Sr.</th>  
  37.                     <th>Name</th>  
  38.                     <th>Email</th>  
  39.                     <th>Phone</th>  
  40.                     <th>Option</th>  
  41.                 </tr>  
  42.                 <tr *ngFor="let item of users;let sl = index">  
  43.                     <td>{{sl+1}}</td>  
  44.                     <td>{{item.firstName}} {{item.lastName}}</td>  
  45.                     <td>{{item.email}}</td>  
  46.                     <td>{{item.phone}}</td>  
  47.                     <td>  
  48.                         <a href="#" title="Edit Record" class="btn btn-primary btn-xs pull-right" (click)="edit($event, item)">  
  49.                             Edit  
  50.                         </a>  
  51.                         <a href="#" title="Delete Record" class="btn btn-danger btn-xs pull-right" (click)="delete($event, item)">  
  52.                             Delete  
  53.                         </a>  
  54.                     </td>  
  55.                 </tr>  
  56.             </table>  
  57.         </div>  
  58.     </div>  
  59. </div>  

Let’s create a typescript model class, then use it in another component by importing like import { UserModel } from './model';

Typescript Model : UserModel

  1. export class UserModel {  
  2.     id: number;  
  3.     firstName: string;  
  4.     lastName: string;  
  5.     phone: string;  
  6.     email: string;  
  7. }  

Below is our UserComponent that is interacting with UI, validating form then sending/receiving data from service component.

Import the component function from Angular6 library; use of "export"  means app component class can be imported from other components.

Component : UserComponent

  1. import { Component, OnInit } from '@angular/core';  
  2. import { FormGroup, FormControl, Validators, FormBuilder } from '@angular/forms';  
  3.   
  4. import { UserModel } from './model';  
  5. import { UserService } from './service';  
  6.   
  7. @Component({  
  8.     selector: 'user',  
  9.     templateUrl: './app/component/user/user.html',  
  10.     providers: [UserService]  
  11. })  
  12.   
  13. export class UserComponent implements OnInit {  
  14.     public user: UserModel;  
  15.     public users: UserModel[];  
  16.     public resmessage: string;  
  17.     userForm: FormGroup;  
  18.   
  19.     constructor(private formBuilder: FormBuilder, private userService: UserService) { }  
  20.   
  21.     ngOnInit() {  
  22.         this.userForm = this.formBuilder.group({  
  23.             id: 0,  
  24.             firstName: new FormControl('', Validators.required),  
  25.             lastName: new FormControl('', Validators.required),  
  26.             email: new FormControl('', Validators.compose([  
  27.                 Validators.required,  
  28.                 Validators.pattern('^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$')  
  29.             ])),  
  30.             phone: new FormControl('', Validators.required)  
  31.         });  
  32.         this.getAll();  
  33.     }  
  34.     onSubmit() {  
  35.         if (this.userForm.invalid) {  
  36.             return;  
  37.         }  
  38.     }  
  39.   
  40.     //Get All User  
  41.     getAll() {  
  42.         //debugger  
  43.         this.userService.getall().subscribe(  
  44.             response => {  
  45.                 //console.log(response)  
  46.                 this.users = response;  
  47.             }, error => {  
  48.                 console.log(error);  
  49.             }  
  50.         );  
  51.     }  
  52.   
  53.     //Get by ID  
  54.     edit(e, m) {  
  55.         //debugger  
  56.         e.preventDefault();  
  57.         this.userService.getByID(m.id)  
  58.             .subscribe(response => {  
  59.                 //console.log(response);  
  60.                 this.user = response;  
  61.                 this.userForm.setValue({  
  62.                     id: this.user.id,  
  63.                     firstName: this.user.firstName,  
  64.                     lastName: this.user.lastName,  
  65.                     email: this.user.email,  
  66.                     phone: this.user.phone  
  67.                 });  
  68.             }, error => {  
  69.                 console.log(error);  
  70.             });  
  71.     }  
  72.   
  73.     //Save Form  
  74.     save() {  
  75.         //debugger  
  76.         this.userService.save(this.userForm.value)  
  77.             .subscribe(response => {  
  78.                 //console.log(response)  
  79.                 this.resmessage = response;  
  80.                 this.getAll();  
  81.                 this.reset();  
  82.             }, error => {  
  83.                 console.log(error);  
  84.             });  
  85.     }  
  86.   
  87.     //Delete  
  88.     delete(e, m) {  
  89.         //debugger  
  90.         e.preventDefault();  
  91.         var IsConf = confirm('You are about to delete ' + m.firstName + '. Are you sure?');  
  92.         if (IsConf) {  
  93.             this.userService.delete(m.id)  
  94.                 .subscribe(response => {  
  95.                     //console.log(response)  
  96.                     this.resmessage = response;  
  97.                     this.getAll();  
  98.                 }, error => {  
  99.                     console.log(error);  
  100.                 });  
  101.         }  
  102.     }  
  103.   
  104.     reset() {  
  105.         this.userForm.setValue({  
  106.             id: 0,  
  107.             firstName: null,  
  108.             lastName: null,  
  109.             email: null,  
  110.             phone: null  
  111.         });  
  112.     }  
  113. }  

Below is service component which will get called for each operation performed by UserComponent.

In our UserService we have Http service [Get, Post, Put, Delete] that connect with WebAPI to perform Create, Read, Update & Delete operations.

Http Client Services - UserService 

  1. import { Injectable, Component } from '@angular/core';  
  2. import { HttpModule, Http, Request, RequestMethod, Response, RequestOptions, Headers } from '@angular/http';  
  3. import { Observable, Subject, ReplaySubject } from 'rxjs';  
  4. import { map, catchError } from 'rxjs/operators';  
  5.   
  6. //Model  
  7. import { UserModel } from './model';  
  8.   
  9. @Component({  
  10.     providers: [Http]  
  11. })  
  12.   
  13. @Injectable()  
  14.   
  15. export class UserService {  
  16.     public headers: Headers;  
  17.     public _getUrl: string = '/api/Values/GetUser';  
  18.     public _getByIdUrl: string = '/api/Values/GetByID';  
  19.     public _deleteByIdUrl: string = '/api/Values/DeleteByID';  
  20.     public _saveUrl: string = '/api/Values/Save';  
  21.   
  22.     constructor(private _http: Http) { }  
  23.   
  24.     //Get  
  25.     getall(): Observable<UserModel[]> {  
  26.         return this._http.get(this._getUrl)  
  27.             .pipe(map(res => <UserModel[]>res.json()))  
  28.             .pipe(catchError(this.handleError));  
  29.     }  
  30.   
  31.     //GetByID  
  32.     getByID(id: string): Observable<UserModel> {  
  33.         var getByIdUrl = this._getByIdUrl + '/' + id;  
  34.         return this._http.get(getByIdUrl)  
  35.             .pipe(map(res => <UserModel>res.json()))  
  36.             .pipe(catchError(this.handleError));  
  37.     }  
  38.   
  39.     //Post  
  40.     save(user: UserModel): Observable<string> {  
  41.         let body = JSON.stringify(user);  
  42.         let headers = new Headers({ 'Content-Type''application/json' });  
  43.         let options = new RequestOptions({ headers: headers });  
  44.         return this._http.post(this._saveUrl, body, options)  
  45.             .pipe(map(res => res.json().message))  
  46.             .pipe(catchError(this.handleError));  
  47.     }  
  48.   
  49.     //Delete  
  50.     delete(id: string): Observable<string> {  
  51.         var deleteByIdUrl = this._deleteByIdUrl + '/' + id  
  52.         return this._http.delete(deleteByIdUrl)  
  53.             .pipe(map(response => response.json().message))  
  54.             .pipe(catchError(this.handleError));  
  55.     }  
  56.   
  57.     private handleError(error: Response) {  
  58.         return Observable.throw(error.json().error || 'Opps!! Server error');  
  59.     }  
  60. }  

Test in Browser

Now it’s time to build & run the application. Let’s try to perform the CRUD operation using the system. As we can see from the below screenshot data is loading in user page from database.

ASP.NET Core

Summary

In this sample, we had to combine ASP.Net Core & Angular to create the sample SPA app without any CLI, and learned how to start with an empty ASP.Net Core application to serve static HTML page.

We also have taken a deep dive into the latest frontend technology like Angular6 from scratch to build a single page application. We had a short overview on Angular6 dependencies & also learned about the module and components. Then we have performed some database operation using our sample application. Hope this will help.