How to Build a real-time Angular 8 Application - Full instructions

How to Build a real-time Angular 8 Application - Full instructions

In this article, we will create a fully functioned employee application. We will create the backend of this application in ASP.NET Core using SignalR. We will use Cosmos DB to save and retrieve the employee data. We will create a SignalR hub and broadcast message to the connected clients. We will then create an Angular 8 application using Angular CLI and create all components for listing, inserting and editing employee data.

In this article, we will create a fully functioned employee application. We will create the backend of this application in ASP.NET Core using SignalR. We will use Cosmos DB to save and retrieve the employee data. We will create a SignalR hub and broadcast message to the connected clients. We will then create an Angular 8 application using Angular CLI and create all components for listing, inserting and editing employee data. We will get the broadcasted message from SignalR hub and immediately show the updated data on client screen. Whenever a user adds/edit/delete an employee record, it will immediately reflect on all other browsers without any user intervention. We can see all the actions step by step.

Create a database and collection in Cosmos DB

As we are saving and retrieving data to Cosmos DB, we can use Cosmos DB emulator to test our application locally. Emulator is fully free, and you can download the MSI setup from this link.

After the download and installation of the emulator, you can run it and create a new database and a collection for your application.

You can see a URI and Primary key in this emulator. We will use these values later with our application to connect Cosmos DB. You can click the Explorer tab and create a new database and a collection.

Since we are creating an Employee application; we need to create a collection named “Employee”. (In Azure Cosmos DB, collections are now called as containers)

We can give a name to our database and collection id as well. You must give a Partition key. A Partition key is very important in Cosmos DB in order to store the data.

Please note, you can’t change the partition key once you created the collection. You must be very careful to choose the correct partition key. Unlike SQL database, there is no need to create other schemas in design time. Data will be saved in JSON format with appropriate field names in run time.

Create an ASP.NET Core Web API project in Visual Studio

We can create an ASP.NET core web application project using Web API template. I am using visual studio 2019 version.

We need two external libraries in this project. One for Cosmos DB and another for SignalR. Both are provided by Microsoft only. We can install these libraries one by one using “Manage NuGet Packages” option.

First, we can install “Microsoft.Azure.DocumentDB.Core” library for Cosmos DB. You can choose the latest version and install.

Then, we can install “Microsoft.AspNet.SignalR” library. This will be used for broadcasting messages to the connected clients real-time.

We can create an interface for SignalR hub client.

IHubClient.cs

using System.Threading.Tasks;  
  
namespace SignalREmployee  
{  
    public interface IHubClient  
    {  
        Task BroadcastMessage();  
    }  
}  

We have created a simple method declaration inside above interface. Please note, in this application we will just notify the client, when a transaction update is happened. You can pass any type of message as a parameter also.

We can create a class “BroadcastHub” and inherit “Hub” class with above interface “IHubClient”

BroadcastHub.cs

using Microsoft.AspNetCore.SignalR;  
  
namespace SignalREmployee  
{  
    public class BroadcastHub : Hub<IHubClient>  
    {  
    }  
}  

We will use this class in our web api controller class and broadcast message to connected clients after updating the employee data in create, update, and delete events later.

Create Cosmos DB Web API service with Repository pattern

We can create the Web API service for our angular application in ASP.NET core with Cosmos DB database. Since we are creating Employee application, we can create an “Employee” model class first.

Create a “Data” folder in the root and create “Employee” class inside it. You can copy the below code and paste to this class file.

Employee.cs

using Newtonsoft.Json;  
  
namespace SignalREmployee.Data  
{  
    public class Employee  
    {  
        [JsonProperty(PropertyName = "id")]  
        public string Id { get; set; }  
        public string Name { get; set; }  
        public string Address { get; set; }  
        public string Gender { get; set; }  
        public string Company { get; set; }  
        public string Designation { get; set; }  
        public string Cityname { get; set; }  
    }  
}  

I have added all the field names required for our Cosmos DB collection. Also note that I have added a “JsonProperty” attribute for “Id” property. Because, Cosmos DB automatically creates an “id” field for each record.

We can create an “IDocumentDBRepository” interface inside the “Data” folder. This interface contains all the method names for our Cosmos DB repository. We will implement this interface in the “DocumentDBRepository” class.

IDocumentDBRepository.cs

using Microsoft.Azure.Documents;  
using System;  
using System.Collections.Generic;  
using System.Linq.Expressions;  
using System.Threading.Tasks;  
  
namespace SignalREmployee.Data  
{  
    public interface IDocumentDBRepository<T> where T : class  
    {  
        Task<Document> CreateItemAsync(T item, string collectionId);  
        Task DeleteItemAsync(string id, string collectionId, string partitionKey);  
        Task<IEnumerable<T>> GetItemsAsync(Expression<Func<T, bool>> predicate, string collectionId);  
        Task<IEnumerable<T>> GetItemsAsync(string collectionId);  
        Task<Document> UpdateItemAsync(string id, T item, string collectionId);  
    }  
}  

I have added all the methods declaration for CRUD actions for Web API controller in the above interface.

We can implement this interface in the “DocumentDBRepository” class.

DocumentDBRepository.cs

using Microsoft.Azure.Documents;  
using Microsoft.Azure.Documents.Client;  
using Microsoft.Azure.Documents.Linq;  
using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Linq.Expressions;  
using System.Threading.Tasks;  
  
namespace SignalREmployee.Data  
{  
    public class DocumentDBRepository<T> : IDocumentDBRepository<T> where T : class  
    {  
  
        private readonly string Endpoint = "https://localhost:8081/";  
        private readonly string Key = "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==";  
        private readonly string DatabaseId = "SarathCosmosDB";  
        private readonly DocumentClient client;  
  
        public DocumentDBRepository()  
        {  
            client = new DocumentClient(new Uri(Endpoint), Key);  
        }  
  
        public async Task<IEnumerable<T>> GetItemsAsync(Expression<Func<T, bool>> predicate, string collectionId)  
        {  
            IDocumentQuery<T> query = client.CreateDocumentQuery<T>(  
                UriFactory.CreateDocumentCollectionUri(DatabaseId, collectionId),  
                new FeedOptions { MaxItemCount = -1 })  
                .Where(predicate)  
                .AsDocumentQuery();  
  
            List<T> results = new List<T>();  
            while (query.HasMoreResults)  
            {  
                results.AddRange(await query.ExecuteNextAsync<T>());  
            }  
  
            return results;  
        }  
  
        public async Task<IEnumerable<T>> GetItemsAsync(string collectionId)  
        {  
            IDocumentQuery<T> query = client.CreateDocumentQuery<T>(  
                UriFactory.CreateDocumentCollectionUri(DatabaseId, collectionId),  
                new FeedOptions { MaxItemCount = -1 })  
                .AsDocumentQuery();  
  
            List<T> results = new List<T>();  
            while (query.HasMoreResults)  
            {  
                results.AddRange(await query.ExecuteNextAsync<T>());  
            }  
  
            return results;  
        }  
  
        public async Task<Document> CreateItemAsync(T item, string collectionId)  
        {  
            return await client.CreateDocumentAsync(UriFactory.CreateDocumentCollectionUri(DatabaseId, collectionId), item);  
        }  
  
        public async Task<Document> UpdateItemAsync(string id, T item, string collectionId)  
        {  
            return await client.ReplaceDocumentAsync(UriFactory.CreateDocumentUri(DatabaseId, collectionId, id), item);  
        }  
  
        public async Task DeleteItemAsync(string id, string collectionId, string partitionKey)  
        {  
            await client.DeleteDocumentAsync(UriFactory.CreateDocumentUri(DatabaseId, collectionId, id),  
            new RequestOptions() { PartitionKey = new PartitionKey(partitionKey) });  
        }  
    }  
}  

I have implemented all the CRUD actions inside the above class. We can use these methods in our Web API controller.

We can create “EmployeesController” controller class now.

EmployeesController.cs

using Microsoft.AspNetCore.Mvc;  
using Microsoft.AspNetCore.SignalR;  
using SignalREmployee.Data;  
using System.Collections.Generic;  
using System.Threading.Tasks;  
  
namespace SignalREmployee.Controllers  
{  
    [Route("api/[controller]")]  
    [ApiController]  
    public class EmployeesController : ControllerBase  
    {  
        private readonly IDocumentDBRepository<Employee> Respository;  
        private readonly IHubContext<BroadcastHub, IHubClient> _hubContext;  
        private readonly string CollectionId;  
  
        public EmployeesController(  
            IDocumentDBRepository<Employee> Respository,   
            IHubContext<BroadcastHub, IHubClient> hubContext)  
        {  
            _hubContext = hubContext;  
            this.Respository = Respository;  
            CollectionId = "Employee";  
        }  
  
        [HttpGet]  
        public async Task<IEnumerable<Employee>> Get()  
        {  
            return await Respository.GetItemsAsync(CollectionId);  
        }  
  
        [HttpGet("{id}/{cityname}")]  
        public async Task<Employee> Get(string id, string cityname)  
        {  
            var employees = await Respository.GetItemsAsync(d => d.Id == id && d.Cityname == cityname, CollectionId);  
            Employee employee = new Employee();  
            foreach (var emp in employees)  
            {  
                employee = emp;  
                break;  
            }  
            return employee;  
        }  
  
        [HttpPost]  
        public async Task<bool> Post([FromBody]Employee employee)  
        {  
            try  
            {  
                if (ModelState.IsValid)  
                {  
                    employee.Id = null;  
                    await Respository.CreateItemAsync(employee, CollectionId);  
                    await _hubContext.Clients.All.BroadcastMessage();  
                }  
                return true;  
            }  
            catch  
            {  
                return false;  
            }  
  
        }  
  
        [HttpPut]  
        public async Task<bool> Put([FromBody]Employee employee)  
        {  
            try  
            {  
                if (ModelState.IsValid)  
                {  
                    await Respository.UpdateItemAsync(employee.Id, employee, CollectionId);  
                    await _hubContext.Clients.All.BroadcastMessage();  
                }  
                return true;  
            }  
            catch  
            {  
                return false;  
            }  
        }  
  
        [HttpDelete("{id}/{cityname}")]  
        public async Task<bool> Delete(string id, string cityname)  
        {  
            try  
            {  
                await Respository.DeleteItemAsync(id, CollectionId, cityname);  
                await _hubContext.Clients.All.BroadcastMessage();  
                return true;  
            }  
            catch  
            {  
                return false;  
            }  
        }  
    }  
}  

I have implemented all the action methods inside this controller class using DocumentDBRepository class. Also note that, we have injected repository class and IHubContext of SignalR in the controller constructor.

We have also called the “BroadcastMessage” in Post, Put, and Delete methods after saving the data to Cosmos DB database using repository.

We are calling above API service from Angular application. Web API application and Angular application are running on different ports or different servers (in production). Hence, we can add CORS headers in Startup class. We must create a client notification service using SignalR also in this class.

We can inject the dependency to DocumentDBRepository service inside Startup class using a singleton pattern.

Startup.cs

using Microsoft.AspNetCore.Builder;  
using Microsoft.AspNetCore.Hosting;  
using Microsoft.AspNetCore.Mvc;  
using Microsoft.Extensions.Configuration;  
using Microsoft.Extensions.DependencyInjection;  
using SignalREmployee.Data;  
  
namespace SignalREmployee  
{  
    public class Startup  
    {  
        public Startup(IConfiguration configuration)  
        {  
            Configuration = configuration;  
        }  
  
        public IConfiguration Configuration { get; }  
  
        // This method gets called by the runtime. Use this method to add services to the container.  
        public void ConfigureServices(IServiceCollection services)  
        {  
            services.AddCors(o => o.AddPolicy("CorsPolicy", builder => {  
                builder  
                .AllowAnyMethod()  
                .AllowAnyHeader()  
                .AllowCredentials()  
                .WithOrigins("http://localhost:4200");  
            }));  
  
            services.AddSignalR();  
  
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);  
  
            services.AddSingleton<IDocumentDBRepository<Employee>>(new DocumentDBRepository<Employee>());  
        }  
  
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.  
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)  
        {  
            if (env.IsDevelopment())  
            {  
                app.UseDeveloperExceptionPage();  
            }  
  
            app.UseCors("CorsPolicy");  
            app.UseSignalR(routes =>  
            {  
                routes.MapHub<BroadcastHub>("/notify");  
            });  
  
            app.UseMvc();  
        }  
    }  
}  

We have modified the “ConfigureServices” and “Configure” methods in above class.

We have successfully created our Web API service. If needed, you can check the API with Postman or any other tool.

Create Angular 8 application using CLI

We can create the angular application with all components using Angular CLI commands. I will explain all the steps one by one.

Use below command to create new Angular application.

ng new AngularEmployee

We are choosing angular routing for this application. It will take some time to create all node modules for our Angular application. Once, our application is ready, we can install below node packages into our application.

npm install @aspnet/signalr

npm install bootstrap

npm install font-awesome

We have now installed signalr client, bootstrap, and font-awesome packages to our application.

We must modify styles.css file in the root folder with below changes to access these packages globally in the application without further references.

styles.css

@import "~bootstrap/dist/css/bootstrap.css";    
@import "~font-awesome/css/font-awesome.css";    

Use below command to create a new “Home” component.

ng g component home

Modify the html template file with below code.

home.component.html

<div style="text-align:center;">  
    <h1>Real-time Angular 8 Application with SignalR and Cosmos DB</h1>  
    <p>Welcome to our new single-page application, built with below technologies:</p>  
    <img src="../../assets/Angular with Cosmos DB and SignalR.png" />  
</div>  

We can create a small navigation menu in our application

ng g component nav-menu

Modify the template file with below code.

nav-menu.component.html

<header>  
    <nav class='navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3'>  
      <div class="container">  
        <a class="navbar-brand" [routerLink]='["/"]'>Employee App</a>  
        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-label="Toggle navigation"  
                [attr.aria-expanded]="isExpanded" (click)="toggle()">  
          <span class="navbar-toggler-icon"></span>  
        </button>  
        <div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse" [ngClass]='{"show": isExpanded}'>  
          <ul class="navbar-nav flex-grow">  
            <li class="nav-item" [routerLinkActive]='["link-active"]' [routerLinkActiveOptions]='{ exact: true }'>  
              <a class="nav-link text-dark" [routerLink]='["/"]'>Home</a>  
            </li>  
            <li class="nav-item" [routerLinkActive]='["link-active"]'>  
              <a class="nav-link text-dark" [routerLink]='["/employees"]'>Employees</a>  
            </li>  
          </ul>  
        </div>  
      </div>  
    </nav>  
  </header>  
  <footer>  
    <nav class="navbar navbar-light bg-white mt-5 fixed-bottom">  
      <div class="navbar-expand m-auto navbar-text">  
        Developed with <i class="fa fa-heart"></i> by <a href="https://codewithsarath.com" target="_blank"><b>Sarathlal Saseendran</b></a>  
      </div>  
    </nav>  
  </footer>  
    

Modify the component file with below code.

nav-menu.component.ts

import { Component, OnInit } from '@angular/core';  
  
@Component({  
  selector: 'app-nav-menu',  
  templateUrl: './nav-menu.component.html',  
  styleUrls: ['./nav-menu.component.css']  
})  
export class NavMenuComponent implements OnInit {  
  
  constructor() { }  
  isExpanded = false;  
  
  ngOnInit() {  
  }  
  
  collapse() {  
    this.isExpanded = false;  
  }  
  
  toggle() {  
    this.isExpanded = !this.isExpanded;  
  }  
}  

We need some styles also in the style file. Modify the style file with below code.

nav-menu.component.css

a.navbar-brand {  
    white-space: normal;  
    text-align: center;  
    word-break: break-all;  
}  
  
html {  
    font-size: 14px;  
}  
  
@media (min-width: 768px) {  
    html {  
        font-size: 16px;  
    }  
}  
  
.box-shadow {  
    box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);  
}  
  
.fa-heart {  
    color: hotpink;  
}  

We can create a generic validator class to validate the employee name.

ng g class shared\generic-validator

Copy below code and paste to the class file.

generic-validator.ts

import { FormGroup } from '@angular/forms';    
    
export class GenericValidator {    
    
  constructor(private validationMessages: { [key: string]: { [key: string]: string } }) {    
  }    
    
  processMessages(container: FormGroup): { [key: string]: string } {    
    const messages = {};    
    for (const controlKey in container.controls) {    
      if (container.controls.hasOwnProperty(controlKey)) {    
        const c = container.controls[controlKey];    
        // If it is a FormGroup, process its child controls.    
        if (c instanceof FormGroup) {    
          const childMessages = this.processMessages(c);    
          Object.assign(messages, childMessages);    
        } else {    
          // Only validate if there are validation messages for the control    
          if (this.validationMessages[controlKey]) {    
            messages[controlKey] = '';    
            if ((c.dirty || c.touched) && c.errors) {    
              Object.keys(c.errors).map(messageKey => {    
                if (this.validationMessages[controlKey][messageKey]) {    
                  messages[controlKey] += this.validationMessages[controlKey][messageKey] + ' ';    
                }    
              });    
            }    
          }    
        }    
      }    
    }    
    return messages;    
  }    
    
  getErrorCount(container: FormGroup): number {    
    let errorCount = 0;    
    for (const controlKey in container.controls) {    
      if (container.controls.hasOwnProperty(controlKey)) {    
        if (container.controls[controlKey].errors) {    
          errorCount += Object.keys(container.controls[controlKey].errors).length;    
          console.log(errorCount);    
        }    
      }    
    }    
    return errorCount;    
  }    
}    

We can create the employee interface using below command.

ng g interface employee\Employee

Copy below code and paste to the interface file.

employee.ts

export interface Employee {  
    id: string,  
    name: string,  
    address: string,  
    gender: string,  
    company: string,  
    designation: string,  
    cityname: string  
}    

We can create the very important Angular service using below command.

ng g service employee\Employee

We can add all the CRUD logic inside this service class.

employee.service.ts

import { Injectable, Inject } from '@angular/core';    
import { HttpClient, HttpHeaders } from '@angular/common/http';    
import { Observable, throwError, of } from 'rxjs';    
import { catchError, map } from 'rxjs/operators';    
import { Employee } from './employee';  
    
@Injectable()    
export class EmployeeService {    
  private employeesUrl = 'http://localhost:57400/api/employees';    
    
  constructor(private http: HttpClient) { }    
    
  getEmployees(): Observable<Employee[]> {    
    return this.http.get<Employee[]>(this.employeesUrl)    
      .pipe(    
        catchError(this.handleError)    
      );    
  }    
    
  getEmployee(id: string, cityName: string): Observable<Employee> {    
    if (id === '') {    
      return of(this.initializeEmployee());    
    }    
    const url = `${this.employeesUrl}/${id}/${cityName}`;    
    return this.http.get<Employee>(url)    
      .pipe(    
        catchError(this.handleError)    
      );    
  }    
    
  createEmployee(employee: Employee): Observable<Employee> {    
    const headers = new HttpHeaders({ 'Content-Type': 'application/json' });    
    return this.http.post<Employee>(this.employeesUrl, employee, { headers: headers })    
      .pipe(    
        catchError(this.handleError)    
      );    
  }    
    
  deleteEmployee(id: string, cityname: string): Observable<{}> {    
    const headers = new HttpHeaders({ 'Content-Type': 'application/json' });    
    const url = `${this.employeesUrl}/${id}/${cityname}`;    
    return this.http.delete<Employee>(url, { headers: headers })    
      .pipe(    
        catchError(this.handleError)    
      );    
  }    
    
  updateEmployee(employee: Employee): Observable<Employee> {    
    const headers = new HttpHeaders({ 'Content-Type': 'application/json' });    
    const url = this.employeesUrl;    
    return this.http.put<Employee>(url, employee, { headers: headers })    
      .pipe(    
        map(() => employee),    
        catchError(this.handleError)    
      );    
  }    
    
  private handleError(err) {    
    let errorMessage: string;    
    if (err.error instanceof ErrorEvent) {    
      errorMessage = `An error occurred: ${err.error.message}`;    
    } else {    
      errorMessage = `Backend returned code ${err.status}: ${err.body.error}`;    
    }    
    console.error(err);    
    return throwError(errorMessage);    
  }    
    
  private initializeEmployee(): Employee {    
    return {    
      id: null,    
      name: null,    
      address: null,    
      gender: null,    
      company: null,    
      designation: null,    
      cityname: null    
    };    
  }    
}    

We are connecting our ASP.NET Core API in this service. Please modify the port number with your own port number.

We can create the “EmployeeList” component to list all employee data.

ng g component employee\EmployeeList

Modify the component file with below code.

employee-list.component.ts

import { Component, OnInit } from '@angular/core';  
import { Employee } from '../employee';  
import { EmployeeService } from '../employee.service';  
import * as signalR from '@aspnet/signalr';  
  
@Component({  
  selector: 'app-employee-list',  
  templateUrl: './employee-list.component.html',  
  styleUrls: ['./employee-list.component.css']  
})  
export class EmployeeListComponent implements OnInit {  
  pageTitle = 'Employee List';  
  filteredEmployees: Employee[] = [];  
  employees: Employee[] = [];  
  errorMessage = '';  
  
  _listFilter = '';  
  get listFilter(): string {  
    return this._listFilter;  
  }  
  set listFilter(value: string) {  
    this._listFilter = value;  
    this.filteredEmployees = this.listFilter ? this.performFilter(this.listFilter) : this.employees;  
  }  
  
  constructor(private employeeService: EmployeeService) { }  
  
  performFilter(filterBy: string): Employee[] {  
    filterBy = filterBy.toLocaleLowerCase();  
    return this.employees.filter((employee: Employee) =>  
      employee.name.toLocaleLowerCase().indexOf(filterBy) !== -1);  
  }  
  
  ngOnInit(): void {  
    this.getEmployeeData();  
  
    const connection = new signalR.HubConnectionBuilder()  
      .configureLogging(signalR.LogLevel.Information)  
      .withUrl("http://localhost:57400/notify")  
      .build();  
  
    connection.start().then(function () {  
      console.log('SignalR Connected!');  
    }).catch(function (err) {  
      return console.error(err.toString());  
    });  
  
    connection.on("BroadcastMessage", () => {  
      this.getEmployeeData();  
    });  
  }  
  
  getEmployeeData() {  
    this.employeeService.getEmployees().subscribe(  
      employees => {  
        this.employees = employees;  
        this.filteredEmployees = this.employees;  
      },  
      error => this.errorMessage = <any>error  
    );  
  }  
  
  deleteEmployee(id: string, name: string, cityname: string): void {  
    if (id === '') {  
      this.onSaveComplete();  
    } else {  
      if (confirm(`Are you sure want to delete this Employee: ${name}?`)) {  
        this.employeeService.deleteEmployee(id, cityname)  
          .subscribe(  
            () => this.onSaveComplete(),  
            (error: any) => this.errorMessage = <any>error  
          );  
      }  
    }  
  }  
  
  onSaveComplete(): void {  
    this.employeeService.getEmployees().subscribe(  
      employees => {  
        this.employees = employees;  
        this.filteredEmployees = this.employees;  
      },  
      error => this.errorMessage = <any>error  
    );  
  }  
  
}    

We have added a SignalR connection in the ngOnInit method and listening to the notification from SignalR server hub. Whenever a new data created/updated/deleted on server side, client will automatically get notification of that change and call the getEmployeeData method. So that, the employee list will be automatically refreshed without any user intervention.

We can modify the html template file with below code.

employee-list.component.html

<div class="card">  
    <div class="card-header">  
        {{pageTitle}}  
    </div>  
    <div class="card-body">  
        <div class="row" style="margin-bottom:15px;">  
            <div class="col-md-2">Filter by:</div>  
            <div class="col-md-4">  
                <input type="text" [(ngModel)]="listFilter" />  
            </div>  
            <div class="col-md-2"></div>  
            <div class="col-md-4">  
                <button class="btn btn-primary mr-3" [routerLink]="['/employees/0/0/edit']">  
                    New Employee  
                </button>  
            </div>  
        </div>  
        <div class="row" *ngIf="listFilter">  
            <div class="col-md-6">  
                <h4>Filtered by: {{listFilter}}</h4>  
            </div>  
        </div>  
        <div class="table-responsive">  
            <table class="table mb-0" *ngIf="employees && employees.length">  
                <thead>  
                    <tr>  
                        <th>Name</th>  
                        <th>Address</th>  
                        <th>Gender</th>  
                        <th>Company</th>  
                        <th>Designation</th>  
                        <th></th>  
                        <th></th>  
                    </tr>  
                </thead>  
                <tbody>  
                    <tr *ngFor="let employee of filteredEmployees">  
                        <td>  
                            <a [routerLink]="['/employees', employee.id,employee.cityname]">  
                                {{ employee.name }}  
                            </a>  
                        </td>  
                        <td>{{ employee.address }}</td>  
                        <td>{{ employee.gender }}</td>  
                        <td>{{ employee.company }}</td>  
                        <td>{{ employee.designation}} </td>  
                        <td>  
                            <button class="btn btn-outline-primary btn-sm"  
                                [routerLink]="['/employees', employee.id, employee.cityname, 'edit']">  
                                Edit  
                            </button>  
                        </td>  
                        <td>  
                            <button class="btn btn-outline-warning btn-sm"  
                                (click)="deleteEmployee(employee.id,  employee.name,employee.cityname);">  
                                Delete  
                            </button>  
                        </td>  
                    </tr>  
                </tbody>  
            </table>  
        </div>  
    </div>  
</div>  
<div *ngIf="errorMessage" class="alert alert-danger">  
    Error: {{ errorMessage }}  
</div>  

Also modify the style file also.

employee-list.component.css

thead {  
    color: #337AB7;  
}  

We can create “EmployeeEdit” component.

ng g component employee\EmployeeEdit

Modify the component with below code.

employee-edit.component.ts

import { Component, OnInit, AfterViewInit, OnDestroy, ElementRef, ViewChildren } from '@angular/core';  
import { FormControlName, FormGroup, FormBuilder, Validators } from '@angular/forms';  
import { Subscription } from 'rxjs';  
import { ActivatedRoute, Router } from '@angular/router';  
import { GenericValidator } from 'src/app/shared/generic-validator';  
import { Employee } from '../employee';  
import { EmployeeService } from '../employee.service';  
  
@Component({  
  selector: 'app-employee-edit',  
  templateUrl: './employee-edit.component.html',  
  styleUrls: ['./employee-edit.component.css']  
})  
export class EmployeeEditComponent implements OnInit, OnDestroy {  
  @ViewChildren(FormControlName, { read: ElementRef }) formInputElements: ElementRef[];  
  pageTitle = 'Employee Edit';  
  errorMessage: string;  
  employeeForm: FormGroup;  
  tranMode: string;  
  employee: Employee;  
  private sub: Subscription;  
  
  displayMessage: { [key: string]: string } = {};  
  private validationMessages: { [key: string]: { [key: string]: string } };  
  private genericValidator: GenericValidator;  
  
  
  constructor(private fb: FormBuilder,  
    private route: ActivatedRoute,  
    private router: Router,  
    private employeeService: EmployeeService) {  
  
    this.validationMessages = {  
      name: {  
        required: 'Employee name is required.',  
        minlength: 'Employee name must be at least three characters.',  
        maxlength: 'Employee name cannot exceed 50 characters.'  
      },  
      cityname: {  
        required: 'Employee city name is required.',  
      }  
    };  
    this.genericValidator = new GenericValidator(this.validationMessages);  
  }  
  
  ngOnInit() {  
    this.tranMode = "new";  
    this.employeeForm = this.fb.group({  
      name: ['', [Validators.required,  
      Validators.minLength(3),  
      Validators.maxLength(50)  
      ]],  
      address: '',  
      cityname: ['', [Validators.required]],  
      gender: '',  
      company: '',  
      designation: '',  
    });  
  
    this.sub = this.route.paramMap.subscribe(  
      params => {  
        const id = params.get('id');  
        const cityname = params.get('cityname');  
        if (id == '0') {  
          const employee: Employee = { id: "0", name: "", address: "", gender: "", company: "", designation: "", cityname: "" };  
          this.displayEmployee(employee);  
        }  
        else {  
          this.getEmployee(id, cityname);  
        }  
      }  
    );  
  }  
  
  ngOnDestroy(): void {  
    this.sub.unsubscribe();  
  }  
  
  getEmployee(id: string, cityname: string): void {  
    this.employeeService.getEmployee(id, cityname)  
      .subscribe(  
        (employee: Employee) => this.displayEmployee(employee),  
        (error: any) => this.errorMessage = <any>error  
      );  
  }  
  
  displayEmployee(employee: Employee): void {  
    if (this.employeeForm) {  
      this.employeeForm.reset();  
    }  
    this.employee = employee;  
    if (this.employee.id == '0') {  
      this.pageTitle = 'Add Employee';  
    } else {  
      this.pageTitle = `Edit Employee: ${this.employee.name}`;  
    }  
    this.employeeForm.patchValue({  
      name: this.employee.name,  
      address: this.employee.address,  
      gender: this.employee.gender,  
      company: this.employee.company,  
      designation: this.employee.designation,  
      cityname: this.employee.cityname  
    });  
  }  
  
  deleteEmployee(): void {  
    if (this.employee.id == '0') {  
      this.onSaveComplete();  
    } else {  
      if (confirm(`Are you sure want to delete this Employee: ${this.employee.name}?`)) {  
        this.employeeService.deleteEmployee(this.employee.id, this.employee.cityname)  
          .subscribe(  
            () => this.onSaveComplete(),  
            (error: any) => this.errorMessage = <any>error  
          );  
      }  
    }  
  }  
  
  saveEmployee(): void {  
    if (this.employeeForm.valid) {  
      if (this.employeeForm.dirty) {  
        const p = { ...this.employee, ...this.employeeForm.value };  
        if (p.id === '0') {  
          this.employeeService.createEmployee(p)  
            .subscribe(  
              () => this.onSaveComplete(),  
              (error: any) => this.errorMessage = <any>error  
            );  
        } else {  
          this.employeeService.updateEmployee(p)  
            .subscribe(  
              () => this.onSaveComplete(),  
              (error: any) => this.errorMessage = <any>error  
            );  
        }  
      } else {  
        this.onSaveComplete();  
      }  
    } else {  
      this.errorMessage = 'Please correct the validation errors.';  
    }  
  }  
  
  onSaveComplete(): void {  
    this.employeeForm.reset();  
    this.router.navigate(['/employees']);  
  }  
}    

Modify the template file with below code.

employee-edit.component.html

<div class="card">    
    <div class="card-header">    
      {{pageTitle}}    
    </div>    
      
    <div class="card-body">    
      <form novalidate    
            (ngSubmit)="saveEmployee()"    
            [formGroup]="employeeForm">    
      
        <div class="form-group row mb-2">    
          <label class="col-md-3 col-form-label"    
                 for="employeeNameId">Employee Name</label>    
          <div class="col-md-7">    
            <input class="form-control"    
                   id="employeeNameId"    
                   type="text"    
                   placeholder="Name (required)"    
                   formControlName="name"    
                   [ngClass]="{'is-invalid': displayMessage.name }" />    
            <span class="invalid-feedback">    
              {{displayMessage.name}}    
            </span>    
          </div>    
        </div>    
      
        <div class="form-group row mb-2">    
          <label class="col-md-3 col-form-label"    
                 for="citynameId">City</label>    
          <div class="col-md-7">    
            <input class="form-control"    
                   id="citynameid"    
                   type="text"    
                   placeholder="Cityname (required)"    
                   formControlName="cityname"    
                   [ngClass]="{'is-invalid': displayMessage.cityname}" />    
            <span class="invalid-feedback">    
              {{displayMessage.cityname}}    
            </span>    
          </div>    
        </div>    
      
        <div class="form-group row mb-2">    
          <label class="col-md-3 col-form-label"    
                 for="addressId">Address</label>    
          <div class="col-md-7">    
            <input class="form-control"    
                   id="addressId"    
                   type="text"    
                   placeholder="Address"    
                   formControlName="address" />    
          </div>    
        </div>    
      
        <div class="form-group row mb-2">    
          <label class="col-md-3 col-form-label"    
                 for="genderId">Gender</label>    
          <div class="col-md-7">    
            <select id="genderId" formControlName="gender" class="form-control">    
              <option value="" disabled selected>Select an Option</option>    
              <option value="Male">Male</option>    
              <option value="Female">Female</option>    
            </select>    
      
          </div>    
        </div>    
      
        <div class="form-group row mb-2">    
          <label class="col-md-3 col-form-label"    
                 for="companyId">Company</label>    
          <div class="col-md-7">    
            <input class="form-control"    
                   id="companyId"    
                   type="text"    
                   placeholder="Company"    
                   formControlName="company" />    
          </div>    
        </div>    
      
        <div class="form-group row mb-2">    
          <label class="col-md-3 col-form-label"    
                 for="designationId">Designation</label>    
          <div class="col-md-7">    
            <input class="form-control"    
                   id="designationId"    
                   type="text"    
                   placeholder="Designation"    
                   formControlName="designation" />    
          </div>    
        </div>    
      
        <div class="form-group row mb-2">    
          <div class="offset-md-2 col-md-6">    
            <button class="btn btn-primary mr-3"    
                    style="width:80px;"    
                    type="submit"    
                    [title]="employeeForm.valid ? 'Save your entered data' : 'Disabled until the form data is valid'"    
                    [disabled]="!employeeForm.valid">    
              Save    
            </button>    
            <button class="btn btn-outline-secondary mr-3"    
                    style="width:80px;"    
                    type="button"    
                    title="Cancel your edits"    
                    [routerLink]="['/employees']">    
              Cancel    
            </button>    
            <button class="btn btn-outline-warning" *ngIf="pageTitle != 'Add Employee'"    
                    style="width:80px"    
                    type="button"    
                    title="Delete this product"    
                    (click)="deleteEmployee()">    
              Delete    
            </button>    
          </div>    
        </div>    
      </form>    
    </div>    
      
    <div class="alert alert-danger"    
         *ngIf="errorMessage">{{errorMessage}}    
    </div>    
  </div>   

We can create an Angular guard to protect accidental data loss while editing the employee data. Use below command to create a guard.

ng g guard employee\EmployeeEdit

Modify the class with below code.

employee-edit.guard.ts

import { Injectable } from '@angular/core';    
import { CanDeactivate } from '@angular/router';    
import { Observable } from 'rxjs';    
import { EmployeeEditComponent } from './employee-edit/employee-edit.component';  
    
    
@Injectable({    
  providedIn: 'root'    
})    
export class EmployeeEditGuard implements CanDeactivate<EmployeeEditComponent> {    
  canDeactivate(component: EmployeeEditComponent): Observable<boolean> | Promise<boolean> | boolean {    
    if (component.employeeForm.dirty) {    
      const name = component.employeeForm.get('name').value || 'New Employee';    
      return confirm(`Navigate away and lose all changes to ${name}?`);    
    }    
    return true;    
  }    
}     

We can create the final component “EmployeeDetail” to show the employee detail.

ng g component employee\EmployeeDetail

Modify the component file with below code.

employee-detail.component.ts

import { Component, OnInit } from '@angular/core';    
import { ActivatedRoute, Router } from '@angular/router';  
import { Employee } from '../employee';  
import { EmployeeService } from '../employee.service';  
    
@Component({    
  selector: 'app-employee-detail',    
  templateUrl: './employee-detail.component.html',    
  styleUrls: ['./employee-detail.component.css']    
})    
export class EmployeeDetailComponent implements OnInit {    
  pageTitle = 'Employee Detail';    
  errorMessage = '';    
  employee: Employee | undefined;    
    
  constructor(private route: ActivatedRoute,    
    private router: Router,    
    private employeeService: EmployeeService) { }    
    
  ngOnInit() {    
    const id = this.route.snapshot.paramMap.get('id');    
    const cityname = this.route.snapshot.paramMap.get('cityname');    
    if (id && cityname) {    
      this.getEmployee(id, cityname);    
    }    
  }    
    
  getEmployee(id: string, cityName: string) {    
    this.employeeService.getEmployee(id, cityName).subscribe(    
      employee => this.employee = employee,    
      error => this.errorMessage = <any>error);    
  }    
    
  onBack(): void {    
    this.router.navigate(['/employees']);    
  }    
}    

Also modify the html template file with below code.

employee-detail.component.html

<div class="card">    
    <div class="card-header"    
         *ngIf="employee">    
      {{pageTitle + ": " + employee.name}}    
    </div>    
    <div class="card-body"    
         *ngIf="employee">    
      <div class="row">    
        <div class="col-md-8">    
          <div class="row">    
            <div class="col-md-3">Name:</div>    
            <div class="col-md-6">{{employee.name}}</div>    
          </div>    
          <div class="row">    
            <div class="col-md-3">City:</div>    
            <div class="col-md-6">{{employee.cityname}}</div>    
          </div>    
          <div class="row">    
            <div class="col-md-3">Address:</div>    
            <div class="col-md-6">{{employee.address}}</div>    
          </div>    
          <div class="row">    
            <div class="col-md-3">Gender:</div>    
            <div class="col-md-6">{{employee.gender}}</div>    
          </div>    
          <div class="row">    
            <div class="col-md-3">Company:</div>    
            <div class="col-md-6">{{employee.company}}</div>    
          </div>    
          <div class="row">    
            <div class="col-md-3">Designation:</div>    
            <div class="col-md-6">{{employee.designation}}</div>    
          </div>    
        </div>    
      </div>    
      <div class="row mt-4">    
        <div class="col-md-4">    
          <button class="btn btn-outline-secondary mr-3"    
                  style="width:80px"    
                  (click)="onBack()">    
            <i class="fa fa-chevron-left"></i> Back    
          </button>    
          <button class="btn btn-outline-primary"    
                  style="width:80px"    
                  [routerLink]="['/employees', employee.id, employee.cityname, 'edit']">    
            Edit    
          </button>    
        </div>    
      </div>    
    </div>    
    <div class="alert alert-danger"    
         *ngIf="errorMessage">    
      {{errorMessage}}    
    </div>    
  </div>   

Since, we are using Angular reactive forms in this application, we must import “ReactiveFormsModule” and “FormsModule” in the app.module.ts file. Also import the “HttpClientModule” for using http client service.

Please include the “EmployeeService” in the providers list also.

app.module.ts

import { BrowserModule } from '@angular/platform-browser';  
import { NgModule } from '@angular/core';  
  
import { AppRoutingModule } from './app-routing.module';  
import { AppComponent } from './app.component';  
import { HomeComponent } from './home/home.component';  
import { NavMenuComponent } from './nav-menu/nav-menu.component';  
import { EmployeeListComponent } from './employee/employee-list/employee-list.component';  
import { EmployeeEditComponent } from './employee/employee-edit/employee-edit.component';  
import { EmployeeDetailComponent } from './employee/employee-detail/employee-detail.component';  
import { EmployeeService } from './employee/employee.service';  
  
import { ReactiveFormsModule, FormsModule } from '@angular/forms';  
import { HttpClientModule } from '@angular/common/http';  
  
@NgModule({  
  declarations: [  
    AppComponent,  
    HomeComponent,  
    NavMenuComponent,  
    EmployeeListComponent,  
    EmployeeEditComponent,  
    EmployeeDetailComponent  
  ],  
  imports: [  
    BrowserModule,  
    AppRoutingModule,  
    ReactiveFormsModule,    
    FormsModule,    
    HttpClientModule,    
  ],  
  providers: [  
    EmployeeService  
  ],  
  bootstrap: [AppComponent]  
})  
export class AppModule { }  

We can modify the routing module with below code also. So that, all routing will work as expected.

app-routing.module.ts

import { NgModule } from '@angular/core';  
import { Routes, RouterModule } from '@angular/router';  
import { HomeComponent } from './home/home.component';  
import { EmployeeListComponent } from './employee/employee-list/employee-list.component';  
import { EmployeeEditGuard } from './employee/employee-edit.guard';  
import { EmployeeDetailComponent } from './employee/employee-detail/employee-detail.component';  
import { EmployeeEditComponent } from './employee/employee-edit/employee-edit.component';  
  
const routes: Routes = [  
  { path: '', component: HomeComponent, pathMatch: 'full' },  
  {  
    path: 'employees',  
    component: EmployeeListComponent  
  },  
  {  
    path: 'employees/:id/:cityname',  
    component: EmployeeDetailComponent  
  },  
  {  
    path: 'employees/:id/:cityname/edit',  
    canDeactivate: [EmployeeEditGuard],  
    component: EmployeeEditComponent  
  },  
]  
  
@NgModule({  
  imports: [RouterModule.forRoot(routes)],  
  exports: [RouterModule]  
})  
export class AppRoutingModule { }  

We can modify the app component html template file with below code.

app.component.html

<body>  
  <app-nav-menu></app-nav-menu>  
  <div class="container">  
    <router-outlet></router-outlet>  
  </div>  
</body>  

We have completed the Angular side coding as well. We can run both ASP.NET core web api project along with Angular project. Please make sure that, Cosmos DB emulator is also working properly.

We can click the Employee tab and add a new employee record.

If you open another browser at the same time, you can see that new employee data will be shown in the employee list of that browser also.

I have opened the application in Chrome and Firefox browsers.

You can perform the edit and delete actions with our application as well.

Conclusion

In this post, we have created an ASP.NET core web API application with SignalR real-time broadcasting. We have used Cosmos DB to save and retrieve the employee information. We have broadcasted a message to all connected clients from server using SignalR hub. We have created an Angular application with all components and route guard using Angular CLI. We have successfully executed ASP.NET Core and Angular applications and created an employee record and saw the real-time data refresh in another browser also.

I hope this tutorial will surely help and you if you liked this tutorial, please consider sharing it with others. Thank you !

Angular and ASP.NET Core

Angular and ASP.NET Core

​ The&nbsp;[Angular CLI](https://cli.angular.io/ "Angular CLI")&nbsp;provides a way to develop front-end applications using angular that hides a lot of details. For example there's no requirement to understand how&nbsp;[Webpack](https://webpack.js.org/ "Webpack")&nbsp;or&nbsp;[SystemJS](https://github.com/systemjs/systemjs "SystemJS")&nbsp;work. ​ In fact, if you don't know a little bit about Webpack, which is what is used to build the latest version of Angular applications, the CLI almost looks like magic. You just need to do a&nbsp;ng new&nbsp;and&nbsp;ng serve --open&nbsp;and you have a working Angular application open in your web browser. ​ The fact that the CLI hides all the plumbing might lead to questions like: "How do I use Angular with ASP.NET Core?". ​ ![](https://res.cloudinary.com/practicaldev/image/fetch/s--dKBafg3O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://www.blinkingcaret.com/wp-content/uploads/2018/01/asp_net_core_and_angular_logo.png) ​ I hope that by the end of this blog post it will be clear to you how you can answer that question (and not only with ASP.NET Core, with whichever technology you want to use your Angular app with). ​ You see, an angular app is an app in and of itself, it does need to be "served" somehow by a web server. ​ When you compile an angular application you are producing a set of JavaScript, CSS and one index.html file. That's it. ​ The default folder where those "artifacts" get copied to is&nbsp;yourApplicationFolder/dist. You can check it out by going to your Angular application and doing an&nbsp;ng build. ​ Go on, I'll wait. ​ When you do&nbsp;ng serve --open&nbsp;you are actually using a stand-alone web server ([webpack-dev-server](https://github.com/webpack/webpack-dev-server "webpack-dev-server")) to serve that index.html file in the dist folder. ​ The rest of this blog post will describe several approaches that you can take for using Angular with ASP.NET Core. The first is to have ASP.NET Core serve the Angular files. ​ The second approach is to have Angular and ASP.NET Core as different applications. There's an example of how to achieve this using Nginx where both Angular and ASP.NET Core are served using port 80 and in IIS where each application is served from its own port. ​ The final part of the post describes a setup that I consider ideal where you can use Angular's&nbsp;ng serve&nbsp;during development. ​ This post is quite long but the sections are fairly independent. If your are only interested in the last section and you are using Windows I recommend also reading the section on how to configure Angular in IIS. ## Using ASP.NET Core to serve the Angular application ​ It can be argued that serving an Angular application "within" ASP.NET Core is wasteful in terms of resources. In the end the Angular application is just a set of static files, there's no need to have the request for those files go through the ASP.NET Core middleware pipeline. ​ There might be some good reasons for doing it though, also there's no harm in knowing how to do it and since it seems to be a common approach, being familiar with it might be useful. ​ One important thing to know in order to understand how we can serve an ASP.NET Core and Angular application together is to understand how a request is processed in ASP.NET Core. ​ When you run an ASP.NET Core application your request goes through a "pipeline" of&nbsp;[middlewares](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware?tabs=aspnetcore2x "middlewares"). Every time a request comes in it goes through the middlewares in the order they are defined, and then in reverse order. ​ Every middleware has an opportunity to change the request or response two times, once before the other middlewares have been executed, and then after the other middlewares have executed. This allows for a middleware at the top of the pipeline to handle for example, a 401 response set by a middleware further down in the pipeline. ​ An example of this are the authentication middlewares that change a 401 response to a 302 redirect to a login page. The Angular CLI provides a way to develop front-end applications using angular that hides a lot of details. For example there’s no requirement to understand how Webpack or SystemJS work.

The Angular CLI provides a way to develop front-end applications using angular that hides a lot of details. For example there's no requirement to understand how Webpack or SystemJS work.

In fact, if you don't know a little bit about Webpack, which is what is used to build the latest version of Angular applications, the CLI almost looks like magic. You just need to do a ng new and ng serve --open and you have a working Angular application open in your web browser.

The fact that the CLI hides all the plumbing might lead to questions like: "How do I use Angular with ASP.NET Core?".

I hope that by the end of this blog post it will be clear to you how you can answer that question (and not only with ASP.NET Core, with whichever technology you want to use your Angular app with).

You see, an angular app is an app in and of itself, it does need to be "served" somehow by a web server.

When you compile an angular application you are producing a set of JavaScript, CSS and one index.html file. That's it.

The default folder where those "artifacts" get copied to is yourApplicationFolder/dist. You can check it out by going to your Angular application and doing an ng build.

Go on, I'll wait.

When you do ng serve --open you are actually using a stand-alone web server (webpack-dev-server) to serve that index.html file in the dist folder.

The rest of this blog post will describe several approaches that you can take for using Angular with ASP.NET Core. The first is to have ASP.NET Core serve the Angular files.

The second approach is to have Angular and ASP.NET Core as different applications. There's an example of how to achieve this using Nginx where both Angular and ASP.NET Core are served using port 80 and in IIS where each application is served from its own port.

The final part of the post describes a setup that I consider ideal where you can use Angular's ng serve during development.

This post is quite long but the sections are fairly independent. If your are only interested in the last section and you are using Windows I recommend also reading the section on how to configure Angular in IIS.

Using ASP.NET Core to serve the Angular application

It can be argued that serving an Angular application "within" ASP.NET Core is wasteful in terms of resources. In the end the Angular application is just a set of static files, there's no need to have the request for those files go through the ASP.NET Core middleware pipeline.

There might be some good reasons for doing it though, also there's no harm in knowing how to do it and since it seems to be a common approach, being familiar with it might be useful.

One important thing to know in order to understand how we can serve an ASP.NET Core and Angular application together is to understand how a request is processed in ASP.NET Core.

When you run an ASP.NET Core application your request goes through a "pipeline" of middlewares. Every time a request comes in it goes through the middlewares in the order they are defined, and then in reverse order.

Every middleware has an opportunity to change the request or response two times, once before the other middlewares have been executed, and then after the other middlewares have executed. This allows for a middleware at the top of the pipeline to handle for example, a 401 response set by a middleware further down in the pipeline.

An example of this are the authentication middlewares that change a 401 response to a 302 redirect to a login page.

You can find the definition of this pipeline on the Startup.cs file, in the Configure method. For example, here's the pipeline that you get when you do a dotnet new mvc:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }

    app.UseStaticFiles();

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

Every time a request comes in to this ASP.NET Core application it can go through at most three middlewares. First the DeveloperExceptionPage/ExceptionHandler middleware depending if the ASP.NET Core application is running in development mode or not. Then the StaticFiles middleware and then finally the Mvc middleware.

The middleware that is key here is StaticFiles. This is the middleware that serves files contained in the wwwroot folder, i.e. if a request comes in for index.html and there's an index.html file at wwwroot/index.html then that file is sent to the client. StaticFiles middleware won't call the middlewares below it after this (in this case it would be Mvc).

You can probably already see how this could work with an Angular application. Just put it under wwwroot.

That's absolutely correct, however there's a detail about StaticFiles that is important to know. StaticFiles won't try to do any guesses for you, i.e. if your request is for /, StaticFiles won't look for /index.html. It will just assume that this request isn't supposed to be handled by it and it will call the next middleware in the pipeline, in this case Mvc.

For this approach to work you need another middleware named DefaultFiles which must come before StaticFiles in the pipeline:

//...
app.UseDefaultFiles();
app.UseStaticFiles();
//...

DefaultFiles will cause cause StaticFiles to look for index.html if the url ends with /.

Now the only thing left to do is to configure your Angular CLI to compile to your ASP.NET Core application's wwwroot folder.

If you look in your Angular's application folder you'll find a .angular-cli.json file. In that file look for the outDir property:

...
"apps": [
{
    ...
    "outDir": "dist",
...

Change it from "dist" to the path of your ASP.NET Core's wwwroot folder. Run ng build in your Angular application and now if you run your ASP.NET Core web application you should see your Angular application in the browser.

A nice development workflow is to run the Angular CLI build in watch mode: In a console window do ng build --watch or ng build -w if you want to save a few key strokes, and leave it running. Now every time you make a change in your Angular application you can just refresh the browser and see the change (you also need to have your ASP.NET Core application running).

There is one thing missing from this approach, though. Deep-linking support, i.e. if your Angular application uses routing and you send a user a url with a valid Angular route (e.g http://yourapplication.com/products/5) the receiving user won't be able to open it. Trying to get to that route will result in a 404 Not Found response.

That's because the request will go all the way through your ASP.NET Core application's pipeline and when it reaches the MVC middleware it won't know what to do with it and will set the response's status code to 404 Page Not Found.

What we can do is at the top of the pipeline we look for a 404 response that is about to be sent and change its path to our Angular application's index.html file (that way what gets served is the Angular application which will know what to do with the url in terms of routing). After this we make the request go through the pipeline again:

//add this at the start of Configure
app.Use(async (HttpContext context, Func<Task> next) =>
{
    await next.Invoke();

    if (context.Response.StatusCode == 404)
    {
        context.Request.Path = new PathString("/index.html");
        await next.Invoke();
    }
});

That fixes deep links but introduces a new problem. What if your web api (that you've implemented in your ASP.NET Core application) needs to send a 404 response. That's something more than reasonable to do. Instead of a 404, the service call will receive a 200 response with index.html.

The solution here is to look at the url and decide if it's intended for the web api or an Angular route. Usually a call to the web api will have /api in the url. That's a simple test to perform and it will solve this problem. Here's the revised version of a custom middleware that solves this problem:

//add this at the start of Configure
app.Use(async (HttpContext context, Func<Task> next) =>
{
    await next.Invoke();

    if (context.Response.StatusCode == 404 && !context.Request.Path.Value.Contains("/api")))
    {
        context.Request.Path = new PathString("/index.html");
        await next.Invoke();
    }
});

One last note about this approach. I've seen examples where the Angular application is in the same Visual Studio solution as the ASP.NET application. Visual Studio (not VS Code) will try to compile the typescript files. If you are using ng build -w you'll want Visual Studio to leave your Typescript files alone. To do that open your project's .csproj and add in any PropertyGroup:

<TypescriptCompileBlocked>true</TypescriptCompileBlocked>

Nginx

Nginx is a web server that can act as a reverse proxy for ASP.NET Core applications and which is also very good at serving static content.

The setup for having an Angular application work with ASP.NET Core is much simpler in Nginx. You just need a configuration similar to this:

server {
    listen 80;        

    location / {
        root /pathToYourAngularApplication/dist;
        index index.html;
        try_files $uri $uri/ /index.html;
    }

    location /api/ {
        proxy_pass http://localhost:5000;
    }
}

This is how a typical Nginx configuration file looks like. If you are not familiar with Nginx and ASP.NET Core I recommend my blog post: HTTPS in ASP.NET Core from Scratch. It has a section with instructions on how to install and setup websites using Nginx.

This configuration allows us to have both the Angular and ASP.NET Core application on port 80. Let's look at the important parts in it.

The listen 80 statement establishes that Nginx will respond to requests coming in on port 80.

The location blocks are where we are going to define how our two applications will be served (Angular and ASP.NET). Each time a request comes in, Nginx will look at the URL and try to find the location block that best matches it. In this case the location blocks urls act like a "prefix match", i.e., the first block will match every URL (every url that starts with a /). The second location block matches URLs that start with /api/.

Nginx picks the most "specific" location block, so even though a request for /api/users would match both location blocks, since the second one (/api/) is more specific, it will be the one that would be used to handle the request.

In the first location block (/):

root /pathToYourAngularApplication/dist sets the path where static content will be looked for as the location where your compiled Angular application files are (dist is the CLI's default output folder).

index index.html specifies which file should be served for URLs that end in /.

try_files $uri $uri/ /index.html can be read this way: check if there's a file that matches the normalized URL (e.g. http://www.yourwebsite.com/assets/image.jpg -> /assets/image.jpg), if that file does not exist try the normalized URL plus a / (e.g. http://www.yourwebsite.com/documents -> /documents/ -> /documents/index.html because of the index rule). If all of that fails serve the file /index.html.

Serving /index.html if no match is found is what enables us to use deep linking. For example a URL such as http://www.yourwebsite.com/documentswith no math in the file system will be served with the Angular application's index.html. Index.html will load all the necessary files for the Angular application to run, specifically the routing module. The routing module will then look at the url, and according to the routes defined in the angular app will decide which component to load.

Finally, the last location block. It instructs Nginx to forward the requests that start with /api/ to a webserver that is listening on port 5000 on localhost. That will be your ASP.NET Core's application.

One note about the Nginx's syntax for proxy_pass. It matters a lot if the URL for the application has a / at the end or not. The url in proxy_pass is treated differently if it has what is described in [Nginx's documentation as a "optional URI"](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass "Nginx's documentation as a "optional URI"") (optional URI isn't a great name, since in the end a URL is a URI).

An example of a URL with an optional URI is: http://localhost:5000/optionalURI/. If the location's path is /api/, then a request for http://yourwebsite.com/api/users will be forwarded to your ASP.NET Core's application as http://localhost:5000/optionalURI/users.

That's why not adding the / at the end in proxy_pass is so important, because if you do (e.g.: proxy_pass http://localhost:5000/;) it falls into the "optional URI" category (it will be interpreted as an empty optional URI), and a request for http://yourwebsite.com/api/users will be seen in your ASP.NET Core's application as a request for http://localhost:5000/users.

If you don't add the / at the end (e.g.: proxy_pass http://localhost:5000;) then a request for http://yourwebsite.com/api/users will be seen in the ASP.NET Core application as a request for http://localhost:5000/api/userswhich is probably what you want.

If you need a more complete example that explains how you can make this work outside a development-time scenario (i.e. have your ASP.NET Core application auto start and remain online even if there's an exception) check out HTTPS in ASP.NET Core from Scratch where there's an example describing how you can use Supervisor to keep the ASP.NET application running even in the event of errors (by auto restarting it).

IIS

With IIS it becomes very cumbersome to have a configuration similar to what we can do with Nginx where both the Angular and ASP.NET Core applications are served on port 80.

To understand why it makes it easier if we understand the IIS concepts of Website and Application. When you create a website you define (among other settings) the port (e.g. 80) where it will be served from. A website can then have several applications "inside" it, all of which will share the website configuration (and therefore be served on the same port).

We could for example put our Angular application inside the "Default Web Site" and the ASP.NET Core one as an IIS Application under it, and call it for example "api".

If the "Default Web Site" responds at http://localhost, then the ASP.NET Core application could be at http://localhost/api. Which seems to be exactly what we want. However, the requests for http://localhost/api would be seen in ASP.NET Core without the api in the url.

As far as I know there's no way to change this behavior.

This means your ASP.NET Core application will behave differently when running inside IIS vs when executed directly (either in Visual Studio or with dotnet run).

To make matters worse an ASP.NET Core application needs to be published (dotnet publish) for it to work in IIS. It's not like a non-Core ASP.NET application where you can just point an IIS Application to the folder that contains the ASP.NET application's files .

So when using IIS the reasonable options are to either have ASP.NET Core serve the angular application as it was described in the first section of this article or have two separate Websites.

Lets walk-though the process of creating two separate websites. First a website for the Angular project and then for ASP.NET Core.

Angular in IIS

We'll be adding a Website named MyNgWebSite on port 80. That means that if you have a "Default Web Site", which in all likelihood you'll have, you need to stop it or change its bindings since the default for it is port 80.

But before we get there we need to create an application pool for our Angular application. Right click on Application Pools in IIS Manager:

The Application Pool for an Angular application does not require Managed Code (we only need to serve static files). We should choose "No Managed Code" in the .NET CLR Version:

We can now add a new IIS web site and set the new application pool we created as its application pool:

The physical path should be set to where your Angular project is being compiled to, usually this is the dist folder.

If you were to try to access http://localhost right now (and assuming that you stopped the "Default Web Site" or used a different port than 80) you would get a permissions error. That's because when you create an application pool a ["virtual" user is created](https://docs.microsoft.com/en-us/iis/manage/configuring-security/application-pool-identities ""virtual" user is created"). That user is a local user and must have permissions to access the folder that contains the files you are trying to serve.

That user's name is IIS AppPool\ApplicationPoolName, in this example it's IIS AppPool\ApplicationPoolForAngular.

Go to the folder that contains the compiled Angular project, right click on it and select properties, go to the security tab, click edit, then add and finally add the application pool user:

We should now be able to access your Angular application if you go to http://localhost.

We still need to do one more thing though. Enable deep-linking support.

If you have routes in your Angular application these won't work if someone tries to access them from "outside" the Angular app. What this means is that if navigating to http://localhost/documents is valid inside the Angular application and you send that url to someone else, when that someone else clicks the link they will be greeted with a 404 page from IIS.

That's because there is no documents folder nor index file inside it for IIS to serve. We need to tell IIS that it must serve the file index.html when someone tries to access a URL that does not exists.

We are going to use the same mechanism used for having a custom 404 page, but instead of a 404 page we'll serve the Angular application.

To achieve this we need to create a web.config file and put it in the src folder of the Angular application with this inside:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <httpErrors errorMode="Custom" existingResponse="Replace">
            <remove statusCode="404"/>
            <error statusCode="404" responseMode="ExecuteURL" path="/index.html"/>
        </httpErrors>
    </system.webServer>
</configuration>

A very quick explanation of what's going on. We are using httpErrors with an errorMode="Custom" and existingResponse="Replace". This instructs IIS to replace the default error pages with the one we are about to specify.

remove statusCode="404" will remove any custom settings for 404 pages if they already exist.

error statusCode="404" responseMode="ExecuteURL" path="/index.html" will configure IIS to execute the /index.html url if there's a 404 error. This will effectively serve your Angular application and won't change the URL seen by the client.

Now we need to edit the .angular-cli.json file so that web.config gets copied to the output folder as an asset when the application is compiled. The assets section is under "app", here's an example:

{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"project": {
    "name": "your-app"
},
"apps": [
    {
    "root": "src",
    "outDir": "dist",
    "assets": [
        "assets",
        "favicon.ico", 
        "web.config"
    ],
    "index": "index.html",
...

ASP.NET Core in IIS

The process for the configuring an ASP.NET Core application in IIS is similar, although we need to select a different port.

But before you start you need to make sure you have the ASP.NET Core Module for IIS installed. It might already be installed if you installed the .Net Core SDK, however the best way to make sure is to go to IIS Manager and see if it's in the modules' list:

If you don't have it you can find more information about it here and a direct link to download it here.

This module takes care of starting and keeping an ASP.NET Core application running.

Before we create the website in IIS we need the published version of the ASP.NET Core application. You can do that in the command line with dotnet publish or, in full Visual Studio, right click on the project and select Publish, then click publish to folder.

Create a new Website and point it to the ASP.NET Core project published folder, give it a different port number (for example 8080) and create an Application Pool for it.

An application pool for an ASP.NET Core application is also unmanaged (No Managed Code). Although this might seem odd, it's because IIS is actually just acting as a reverse proxy.

Before we're able to run the ASP.NET Project using IIS we need to changed the published folder's permissions so that the Application Pool user can access it. If you don't you'll get this moderately unhelpful error message:

HTTP Error 500.19 - Internal Server Error> HTTP Error 500.19 - Internal Server Error

If you look at the Config Error section you'll see "Cannot read configuration file due to insufficient permissions", which pretty much says it all.

Go to the published folder and add the application pool user to the list of users with permissions over that folder.

Your ASP.NET Core application should now be available on the port you've selected when you created the website in IIS. However, if you try to call it from the Angular application you'll get this error "Failed to load ... No 'Access-Control-Allow-Origin' header is present on the requested resource...". Here's an example of how that would look like in the developer tools console tab:

That's because even though both our our Angular and ASP.NET Core applications are on the same domain, they are in different ports, and that's enough to qualify the request as a Cross Origin Resource Sharing (CORS) request in all browsers except IE.

We need to enable CORS on the ASP.NET Core application. To do that we need to add the package Microsoft.AspNetCore.Cors and in ConfigureServices(IServiceCollection services... method in Startup.csadd services.AddCors():

public void ConfigureServices(IServiceCollection services)
{
    //...
    services.AddCors();
    //...
}

And in the Configure method we need to create a "policy" that says that we are expecting requests from http://localhost. We should do that before the MVC middleware:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    //...
    app.UseCors(builder => builder.WithOrigins("http://localhost"));
    app.UseMvc();
}

You should be good to go. Your Angular and ASP.NET Core should both be working now.

Platform Agnostic Development Setup

Both Angular and ASP.NET Core applications provide ways to detect if they are running in development or production mode. That can be leveraged to create a setup that works both in Linux, Windows or Mac.

The easiest way to run an Angular application is to use run ng serve. That spins up a webpack development server that serves the Angular application on port 4200 by default.

This also has the advantage of having hot module replacing, which means you can see your changes to the Angular application as soon as you make then without even having to refresh the browser.

So ideally we want to run the Angular application this way.

For the ASP.NET Core application we want to run it without having to publish it which you would have to if it is being served by IIS.

This is the ideal development scenario, ng serve for Angular and dotnet run or running the ASP.NET Core from Visual Studio without having to publish it.

In this ideal scenario when developing we could have the Angular application running on port 4200 (through ng serve) and the ASP.NET Core application running on port 5000. When in production the Angular application would typically be served from port 80 and the ASP.NET Core application for port 8080 for example (or from a different server on port 80).

On the ASP.NET Core side of things we'd have to configure CORS to accept requests from port 4200 when in development and from port 80 when in production. In Startup.cs that would look like this:

public void ConfigureServices(IServiceCollection services)
{
    services.AddCors();
    //...        
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    //...
    if (env.IsDevelopment())
    {
        //...
        app.UseCors(builder => builder.WithOrigins("http://localhost:4200"));
    }else 
    {
        app.UseCors(builder => builder.WithOrigins("http://localhost"));
    }

    app.UseMvc();
}

That takes care of the ASP.NET Core application.

For Angular we need to leverage the environemnt.ts and environemnt.prod.ts files. You can find then under a folder name environemnts under the src folder on an Angular project.

What you put on environment.ts will be available when you run in development mode (the default) and the values in environment.prod.ts will be used when in production. To compile the Angular project with the environment set to production use the --env=prod flag (e.g. ng build --env=prod).

Here's a simple example of how the environment files could be configured to support our hypothetical scenario, environment.ts:

export const environment = {
    production: false,
    apiBaseUrl: "http://localhost:4200/"
};

environment.prod.ts:

export const environment = {
    production: true,
    apiBaseUrl: "http://localhost/"
};

In your Angular services, to get to the environment values you just need to import the environment (always environment and not environment.prod):

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { environment } from '../environments/environment';

@Injectable()
export class MyServiceService {

    constructor(private httpClient: HttpClient) { }

    getStuff(){
        return this.httpClient.get(`${environment.apiBaseUrl}/api/suff`);
    }  
}

This approach would work even if you host on Nginx or IIS so probably the best option if you need/want to support having developers using different platforms of if you just want to compare performance between them.

ASP.NET MVC tutorial for Azure Cosmos DB: How to use Azure Cosmos DB

ASP.NET MVC tutorial for Azure Cosmos DB: How to use Azure Cosmos DB

In this article, you will learn how to use Azure Cosmos DB - a key-value store solution - your ASP.NET app instead of a relational database.

In this article, you will learn how to use Azure Cosmos DB - a key-value store solution - your ASP.NET app instead of a relational database.

Cosmos DB is a planet-scale database capable of holding billions of records (“documents” using Cosmos jargon) without significant detriment to performance.

To demonstrate their differences, check out a simple example query using both a relational database and Cosmos DB. When you query a relational database you get back a set of rows. All rows have the same columns and some may have relationships to other rows. Take a look:

SELECT * FROM Items WHERE Name = 'WebForm' 

Record Found

When you query Azure Cosmos DB, you get back a CLOB - a character large object - in this case, a JSON file. In the example below c refers to the collection, determined by the URL being queried.

SELECT * FROM c WHERE c.Name = '' 

Record Found

{
    "name": "Netflix",
    "amount": 16,
    "frequency": "m",
    "startDate": "2019-06-01T00:00:00",
    ...
}

While Cosmos is powerful, data with complex relationships may be better off in a relational database than in a key-value store like Azure Cosmos DB. Relationships get tricky with Cosmos and often result in multiple transactions to get the data you need.

What you’ll need for this post:

Scaffold Your ASP.NET Web Project

Let’s get started! Open Visual Studio and add a new ASP.NET Web Application (.NET Framework) project. Give your new project a name that is meaningful to you, I’m calling mine ExpenseWebApp. Then select MVC as the ASP.NET 4.6.1 Template.

Define Object Models for Your ASP.NET + Cosmos DB App

Let’s define our model objects. The following objects will be used to send data through the layers of the app.

Right click on Models and add a new class named Expense.cs. Paste the following code into the new file.

using System;
using Newtonsoft.Json;

namespace ExpenseWebApp.Models
{
  public class Expense
  {
    [JsonProperty(PropertyName = "name")]
    public string Name { get; set; }

    [JsonProperty(PropertyName = "amount")]
    public double Amount { get; set; }

    [JsonProperty(PropertyName = "frequency")]
    public string Frequency { get; set; }

    [JsonProperty(PropertyName = "startDate")]
    public DateTime StartDate { get; set; }

    [JsonProperty(PropertyName = "monthlyCost")]
    public double MonthlyCost { get; set; }
  }
}

Right click on Models and add a new class named Payment.cs. Paste the following code into the new file.

using System;

namespace ExpenseWebApp.Models
{
  public class Payment
  {
    public DateTime DueDate { get; set; }
    public double Amount { get; set; }
  }
}
Create Cosmos DB in Azure

Cosmos DB is where we’ll be storing the data used by your application. The Cosmos portion of this project is divided into two parts - first creating the Cosmos DB, and second programming our ASP.NET App to connect to it.

Login to your Microsoft Azure Portal and go to Azure Cosmos DB under All resources. Click the Add button, then make sure to specify the following settings:

Account Name = expense-cosmos-db API = Core (SQL)

Next click Review + create. Then on the subsequent screen click Create. You will be taken to a screen showing the status of your new Azure Cosmos DB - this may take a few minutes.

When your Cosmos DB is ready to go, click Go to resource which will take you to the Quick start guide. For this tutorial, we’ll use the Items container that Azure creates for you automatically, so go ahead and click Create ‘Items’ container.

Finally, select a resource group. If you don’t already have a resource group you want to put Cosmos DB into, feel free to create a new one.

Before navigating away from Azure, you’ll want to copy some values to your web.config under AppSettings.

Go to to Keys under your new expense–cosmos-db and copy the value from URI to Cosmos.EndPointUrl in your web.config. Then copy the value from PRIMARY KEY to Cosmos.AuthorizationKey in your web.config.





Side note: Data Explorer is where you would go if you want to browse the data in your containers. Although not utilized in this article, you will probably end up using it frequently to examine your data in the future!

Connect Your ASP.NET App to Azure Cosmos DB

Add the following Nuget Package to your project:

Microsoft.Azure.DocumentDB v2.4.0

Right click on Models and add a new class named CosmosConfig.cs. This class will be used to store the Cosmos DB configuration settings. Paste the following code into the new file.

using System;

namespace ExpenseWebApp.Models
{
  public class CosmosConfig
  {
    public string EndPointUrl { get; set; }
    public string AuthorizationKey { get; set; }

    public string DatabaseId { get; set; }
    public string ExpenseCollectionId { get; set; }
  }
}

Right click on Global.asax and select View Code. Overwrite the code that’s currently in the file with the following.

using System.Configuration;
using System.Web;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
using System.Web.Helpers;

using ExpenseWebApp.Models;

namespace ExpenseWebApp
{
  public class MvcApplication : HttpApplication
  {
    protected void Application_Start()
    {
      AreaRegistration.RegisterAllAreas();
      FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
      RouteConfig.RegisterRoutes(RouteTable.Routes);
      BundleConfig.RegisterBundles(BundleTable.Bundles);

      MvcApplication.InitCosmosConfig();
    }

    public static CosmosConfig CosmosConfig { get; set; }

    private static void InitCosmosConfig()
    {
      AntiForgeryConfig.UniqueClaimTypeIdentifier = "name";
      MvcApplication.CosmosConfig = new CosmosConfig()
      {
        DatabaseId = ConfigurationManager.AppSettings["Cosmos.DatabaseId"],
        EndPointUrl = ConfigurationManager.AppSettings["Cosmos.EndPointUrl"],
        AuthorizationKey = ConfigurationManager.AppSettings["Cosmos.AuthorizationKey"],
        ExpenseCollectionId = ConfigurationManager.AppSettings["Cosmos.ExpenseCollectionId"]
      };
    }
  }
}

This new code creates the static CosmosConfig property of MvcApplication to store your Cosmos configuration settings. It also defines InitCosmosConfig() and calls it when the application starts.

The HomeController will do the bulk of the work. Right click on HomeController.cs and select View Code. Overwrite the code in the file with the following.

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Web.Mvc;

using Microsoft.Azure.Documents.Client;
using Microsoft.Azure.Documents.Linq;

using ExpenseWebApp.Models;

namespace ExpenseWebApp.Controllers
{
  public class HomeController : Controller
  {
    public async Task Index()
    {
      ViewBag.Title = "Expenses";

      //get expenses from Cosmos DB
      var expenses = await this.GetExpenses();

      var monthStart = new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1);

      //calculate payments that will take place over 2 months from the start of this month
      var payments = this.GetPayments(expenses, monthStart, monthStart.AddMonths(2));

      //calculate how much is owed over specific time periods
      ViewBag.AlreadyPaid = this.OwedDuring(payments, monthStart, DateTime.Today - new TimeSpan(1, 0, 0, 0));
      ViewBag.DueToday = this.OwedDuring(payments, DateTime.Today, DateTime.Today);
      ViewBag.StillComing = this.OwedDuring(payments, DateTime.Today.AddDays(1), monthStart.AddMonths(1) - new TimeSpan(1, 0, 0, 0));

      ViewBag.Next1Week = this.OwedDuring(payments, DateTime.Today, DateTime.Today.AddDays(7));
      ViewBag.Next2Weeks = this.OwedDuring(payments, DateTime.Today, DateTime.Today.AddDays(14));
      ViewBag.Next1Month = this.OwedDuring(payments, DateTime.Today, DateTime.Today.AddMonths(1));
      ViewBag.Next2Months = this.OwedDuring(payments, DateTime.Today, DateTime.Today.AddMonths(2));

      return View(expenses);
    }

    [HttpPost]
    [ActionName("Create")]
    [ValidateAntiForgeryToken]
    public async Task CreateAsync([Bind(Include = "Name,Amount,Frequency,StartDate")] Expense input)
    {
      if (ModelState.IsValid)
        using (var client = new DocumentClient(new Uri(MvcApplication.CosmosConfig.EndPointUrl), MvcApplication.CosmosConfig.AuthorizationKey))
        {
          await client.CreateDocumentAsync(UriFactory.CreateDocumentCollectionUri(MvcApplication.CosmosConfig.DatabaseId, MvcApplication.CosmosConfig.ExpenseCollectionId), input);
          return RedirectToAction("Index");
        }

      return View(input);
    }


    private List GetPayments(List expenses, DateTime start, DateTime finish)
    {
      var ret = new List();

      //get all payments between specified start and finish dates
      foreach (var e in expenses)
        for (var x = e.StartDate; x <= finish;)
        {
          if (x >= start)
            ret.Add(new Payment() { Amount = e.Amount, DueDate = x });
          x = this.GetNextDueDate(x, e.Frequency);
        }

      return ret;
    }
    private DateTime GetNextDueDate(DateTime lastDueDate, string frequency)
    {
      if (frequency == "w")
        return lastDueDate.AddDays(7);
      else if (frequency == "m")
        return lastDueDate.AddMonths(1);
      else if (frequency == "q")
        return lastDueDate.AddMonths(3);
      else if (frequency == "y")
        return lastDueDate.AddYears(1);

      throw new Exception("Invalid expense frequency - unable to get the next due date");
    }
    private double OwedDuring(List payments, DateTime start, DateTime finish)
    {
      double ret = 0;

      for (var i = 0; i < payments.Count; i++)
        if (payments[i].DueDate >= start && payments[i].DueDate <= finish)
          ret += payments[i].Amount;

      return ret;
    }

    private async Task> GetExpenses()
    {
      var ret = new List();

      using (var client = new DocumentClient(new Uri(MvcApplication.CosmosConfig.EndPointUrl), MvcApplication.CosmosConfig.AuthorizationKey))
      {
        IDocumentQuery query = client.CreateDocumentQuery(
          UriFactory.CreateDocumentCollectionUri(MvcApplication.CosmosConfig.DatabaseId, MvcApplication.CosmosConfig.ExpenseCollectionId),
          new FeedOptions { MaxItemCount = -1 })
          .AsDocumentQuery();

        while (query.HasMoreResults)
          ret.AddRange(await query.ExecuteNextAsync());

        for (var i = 0; i < ret.Count; i++)
          ret[i].MonthlyCost = CalculateMonthlyCost(ret[i]);
      }

      return ret;
    }
    private double CalculateMonthlyCost(Expense input)
    {
      if (input.Frequency == "w")
        return input.Amount * 52 / 12;

      else if (input.Frequency == "m")
        return input.Amount;

      else if (input.Frequency == "q")
        return input.Amount / 3;

      else if (input.Frequency == "y")
        return input.Amount / 12;

      throw new Exception("Invalid expense frequency - unable to calculate the monthly cost");
    }
  }
}

The Index() action will now pass a list of expenses as the model. It also calculates several values passed via the ViewBag such as Next1Week and Next2Weeks which reflect the amount that will be owed over the next week, and next 2 weeks respectively.

Refer to GetExpenses() to see how data is pulled from Azure Cosmos DB.

Refer to GetPayments() to see the algorithm for calculating how many payments will occur over a specified period of time, based on the expenses and how often they recur (IE: weekly, monthly, quarterly, or yearly).

Get Your Cosmos DB Data to the ASP.NET App UI

First things first on the UI, you need to throw together some markup! Open up Views\Home\Index.cshtml and overwrite the code that’s currently in the file with the following.



  # @ViewBag.Title

  
    ### This month...

    Already paid
    <input type="text" readonly="readonly" class="highlight"
    value="@string.Format("{0:C}", ViewBag.AlreadyPaid)" />
    Due today
    <input type="text" readonly="readonly" class="highlight"
    value="@string.Format("{0:C}", ViewBag.DueToday)" />
    Still coming
    <input type="text" readonly="readonly" class="highlight"
    value="@string.Format("{0:C}", ViewBag.StillComing)" />
  
  
    ### Next...

    1 week
    <input type="text" readonly="readonly" class="highlight"
    value="@string.Format("{0:C}", ViewBag.Next1Week)" />
    2 weeks
    <input type="text" readonly="readonly" class="highlight"
    value="@string.Format("{0:C}", ViewBag.Next2Weeks)" />
    1 month
    <input type="text" readonly="readonly" class="highlight"
    value="@string.Format("{0:C}", ViewBag.Next1Month)" />
    2 months
    <input type="text" readonly="readonly" class="highlight"
    value="@string.Format("{0:C}", ViewBag.Next2Months)" />
  



  
  ## List of Expenses

  
    
      @*
      
      *@
      Name
      Monthly Cost
    
    @if (Model != null) { var total = 0; foreach (var e in Model) { total +=
    e.MonthlyCost;
    
      @e.Name
      @string.Format("{0:C}", e.MonthlyCost)
    
    }
    
      Total
      @string.Format("{0:C}", total)
    
    }
  

  
  @using (Html.BeginForm("Create", "Home")) { @Html.AntiForgeryToken()

  ### Add Expense


  Name
  

  Amount
  

  Frequency
  
    Weekly
    Monthly
    Quarterly
    Yearly
  

  Start Date
  

  
  }

This code adds:

  • A summary of upcoming expenses at the top of the page
  • A list of expenses and their monthly costs
  • A form to add new expenses

At this point, you’ll be able to run your app with all the functionality in place, but no styling other than what comes out of the box.

Of course we want more styling, so open up Site.css - located in the Content folder. Overwrite everything from line 19 down, with the following, then re-run your app to see the changes.

/* Set width on the form input elements since they're 100% wide by default */
input,
label,
select,
textarea {
  display: block;
  margin: 0px 0px 0px 0px;
}
label {
  margin-top: 5px;
}

input[type='submit'] {
  margin-top: 20px;
  display: block;
  margin: auto;
}

/* Custom styling */
div.left {
  float: left;
  width: 45%;
  overflow: hidden;
}
div.right {
  overflow: hidden;
}

div.jumbotron input[readonly='readonly'] {
  margin: 0px 0px 0px 0px;
  padding: 0px 0px 0px 0px;
  background-color: transparent;
  border-style: none;
}
input.highlight {
  font-weight: bold;
  color: darkgreen;
}

tr.header td {
  border-style: solid;
  border-width: 1px;
  border-color: #000000;
  padding: 4px 15px 4px 15px;
  text-align: center;
}
tr.header td.empty {
  border-style: none;
}

tr td {
  padding: 4px 4px 4px 4px;
}
tr td.money {
  text-align: right;
  color: darkgreen;
}
tr td.total {
  text-align: right;
  font-weight: bold;
  font-style: italic;
}
Implement Authentication in ASP.NET with Okta

Okta is a developer API service with SDKs and tooling that make it easy to store user accounts and add registration, login, multi-factor auth and more to your app. If you don’t already have one, you’ll need to create a free Okta developer account.

After creating an account, Okta will provide a URL for your organization called an “Okta domain”. Be sure to check your email for instructions to complete your registration.

Once you have logged in, on your administrator dashboard, click Applications in the main menu and on the Application screen, click Add Application.

Select Web then click Next.

On the Create New Application screen, set the values I have shown below, but make sure the port is set correctly. Visual Studio will assign a random port to new applications to make it easy for you to have multiple running at the same time. To see what port your app is using, click Run in visual studio, and replace 59601 in the URLs below, with the port your app is running on.

  • Base URIs: [http://localhost:59601/](http://localhost:59601/)
  • Login redirect URIs: [http://localhost:59601/authorization-code/callback](http://localhost:59601/authorization-code/callback)
  • Initiate login URI: [http://localhost:59601/authorization-code/callback](http://localhost:59601/authorization-code/callback)
  • Allow grant types:
  • Authorization Code
  • Implicit (Hybrid)

After you save your settings, click Edit by General Settings, then check the following two checkboxes as shown in the screenshot below:

  • Allow ID Token with implicit grant type
  • Allow Access Token with implicit grant type

Next, scroll to the bottom and copy your Client ID and Client secret. Paste Client ID into the okta:ClientId appSetting in your web.config. Paste Client secret into the okta.ClientSecret appSetting in your web.config. Also make sure the ports are set correctly for the okta:RedirectUri and okta:PostLogoutRedirectUri appSetting records.






Your Okta account is now setup and ready to go. Now we just have to code it into our app to make it secure.

Secure Your ASP.NET App

To secure your web app with Okta you’ll need to install the following Nuget Packages:

  • Microsoft.Owin.Host.SystemWeb v4.0.1
  • Microsoft.Owin.Security.Cookies v4.0.1
  • Okta.AspNet v1.1.5

Right click on your project and add a new class called Startup.cs. In that file paste the following code:

using System;
using System.Threading.Tasks;
using Microsoft.Owin;
using Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Okta.AspNet;
using System.Collections.Generic;
using System.Configuration;

[assembly: OwinStartup(typeof(ExpenseWebApp.Startup))]

namespace ExpenseWebApp
{
  public class Startup
  {
    public void Configuration(IAppBuilder app)
    {
      app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

      app.UseCookieAuthentication(new CookieAuthenticationOptions());

      app.UseOktaMvc(new OktaMvcOptions()
      {
        OktaDomain = ConfigurationManager.AppSettings["okta:OktaDomain"],
        ClientId = ConfigurationManager.AppSettings["okta:ClientId"],
        ClientSecret = ConfigurationManager.AppSettings["okta:ClientSecret"],
        RedirectUri = ConfigurationManager.AppSettings["okta:RedirectUri"],
        PostLogoutRedirectUri = ConfigurationManager.AppSettings["okta:PostLogoutRedirectUri"],
        GetClaimsFromUserInfoEndpoint = true,
        Scope = new List { "openid", "profile", "email" },
      });
    }
  }
}

Next we’ll need to create the Account controller to handle login and logout events. Add a new file to Controllers called AccountController.cs. In that file paste the following code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Microsoft.Owin.Security.Cookies;
using Okta.AspNet;

namespace ExpenseWebApp.Controllers
{
  public class AccountController : Controller
  {
    public ActionResult Login()
    {
      if (!HttpContext.User.Identity.IsAuthenticated)
      {
        HttpContext.GetOwinContext().Authentication.Challenge(
          OktaDefaults.MvcAuthenticationType);
        return new HttpUnauthorizedResult();
      }

      return RedirectToAction("Index", "Expense");
    }

    [HttpPost]
    public ActionResult Logout()
    {
      if (HttpContext.User.Identity.IsAuthenticated)
      {
        HttpContext.GetOwinContext().Authentication.SignOut(
          CookieAuthenticationDefaults.AuthenticationType,
          OktaDefaults.MvcAuthenticationType);
      }

      return RedirectToAction("Index", "Home");
    }

    public ActionResult PostLogout()
    {
      return RedirectToAction("Index", "Home");
    }
  }
}

Once complete, your security settings are in place and we just need to tell our App which pages to apply the security to. We’ll do this by placing the [Authorize] attribute on the controller we want to be secured. Open up HomeController.cs and paste the following [Authorize] attribute just above the class declaration on line 13, as shown below:

  [Authorize]
  public class HomeController : Controller
  {
    ...
Update Template with User Authentication Status

Open up Views\Shared\_Layout.cshtml and overwrite the contents of lines 22 through 26 with the following:

@if (Context.User.Identity.IsAuthenticated) {
*Hello, **@Context.User.Identity.Name**
* Log out


} else {
* @Html.ActionLink("Log in", "Login", "Account")

}
Run Your ASP.NET MVC App

To run your App, simply click Run in Visual Studio. You will be taken to the Okta login page and then to the Expenses screen after successfully logging in. Your new app is very simple and provides plenty of room for you to continue building out the feature set.

Building a Chat application with Angular 8, SignalR and ASP.NET Core

Building a Chat application with Angular 8, SignalR and ASP.NET Core

In this post, we are going to create a simple chat application using Angular 8, ASP.NET Core and SignalR

In this post, we are going to create a simple chat application using Angular 8, ASP.NET Core 2.2.0, and SignalR 1.1.0 as shown below.

We need to install all the below-mentioned prerequisites on our development machine.

Prerequisite

  • .NET Core SDK 2.2.0 – download from here.
  • Node.js 10.15.3 – download from here.
  • Angular CLI 8.0 – Install Angular command package by executing this command: “npm install -g @angular/[email protected]
Set up ASP.NET Core with SignalR Project

Open the command prompt and enter the following command to create an ASP.NET Core web project as below.

Install the following NuGet package in your project.

  • Microsoft.AspNetCore.SignalR
  • Microsoft.AspNetCore.SpaServices.Extensions

Call SignalR in the “Startup.ConfigureServices” method to add SignalR services.

services.AddSignalR();

Create MessageHub Class

SignalR makes real-time client-to-server and server-to-client communications possible and it will call methods to connect clients from a server using SignalR Hub API. You can find more details about SignalR from here. Now, we need to perform the following steps to create the MessageHub class that is extending SignalR Hub API features.

  • Create Hubs Folder in the project
  • Declare MessageHub class that inherits from SignalR Hub
  • Add the NewMessage public method to get a new message from a connected client.
  • Then, Send an async message to all connected clients.
using ChatApp.Models;  
using Microsoft.AspNetCore.SignalR;  
using System.Threading.Tasks;  
  
namespace ChatApp.Hubs  
{  
    public class MessageHub : Hub  
    {  
        public async Task NewMessage(Message msg)  
        {  
            await Clients.All.SendAsync("MessageReceived", msg);  
        }  
    }  
}  

Then, add route to handle a request in Startup.ConfigureService method for MessageHub.

app.UseSignalR(options =>  
{  
      options.MapHub<MessageHub>("/MessageHub");  
 });  

Add a Message Class

public class Message  
   {  
       public string clientuniqueid { get; set; }  
       public string type { get; set; }  
       public string message { get; set; }  
       public DateTime date { get; set; }  
   }  
Add Angular 8 into the project

We will scaffold Angular into our project. For this, we need to execute “ng new ClientApp --skip-install” on Visual Studio Code terminal. Here, --skip-install option is used to skip installation of the npm packages.

Now, let us enter the “npm install” command to install all Angular npm packages in the terminal. And then, update the SPA static file service configuration to the Angular output files folder location. For this, we will add the below code to the “Startup.Configure” method.

app.UseSpa(spa =>  
            {  
                // To learn more about options for serving an Angular SPA from ASP.NET Core,  
                // see https://go.microsoft.com/fwlink/?linkid=864501  
  
                spa.Options.SourcePath = "ClientApp";  
  
                if (env.IsDevelopment())  
                {  
                    //spa.UseProxyToSpaDevelopmentServer("http://localhost:4200");  
                    spa.UseAngularCliServer(npmScript: "start");  
                }  
            });  

Here, UseAngularCliServer will pass the request through to an instance of Angular CLI server which keeps up-to-date CLI-built resources without having to run the Angular CLI server manually.

Now, add SignalR client library to connect MessageHub from the Angular as below screenshot.

Create Chat Service to connect SignalR

Create chat.service.ts class to establish a connection with Message Hub and to publish & receive chat messages.

Then, we have added chat service as a provider in Ng Modules.

import { EventEmitter, Injectable } from '@angular/core';  
import { HubConnection, HubConnectionBuilder } from '@aspnet/signalr';  
import { Message } from '../models/message';  
  
@Injectable()  
export class ChatService {  
  messageReceived = new EventEmitter<Message>();  
  connectionEstablished = new EventEmitter<Boolean>();  
  
  private connectionIsEstablished = false;  
  private _hubConnection: HubConnection;  
  
  constructor() {  
    this.createConnection();  
    this.registerOnServerEvents();  
    this.startConnection();  
  }  
  
  sendMessage(message: Message) {  
    this._hubConnection.invoke('NewMessage', message);  
  }  
  
  private createConnection() {  
    this._hubConnection = new HubConnectionBuilder()  
      .withUrl(window.location.href + 'MessageHub')  
      .build();  
  }  
  
  private startConnection(): void {  
    this._hubConnection  
      .start()  
      .then(() => {  
        this.connectionIsEstablished = true;  
        console.log('Hub connection started');  
        this.connectionEstablished.emit(true);  
      })  
      .catch(err => {  
        console.log('Error while establishing connection, retrying...');  
        setTimeout(function () { this.startConnection(); }, 5000);  
      });  
  }  
  
  private registerOnServerEvents(): void {  
    this._hubConnection.on('MessageReceived', (data: any) => {  
      this.messageReceived.emit(data);  
    });  
  }  
}    
Create a Chat App Component

app.component.html:

<div class="container">  
  <h3 class=" text-center chat_header">Chat Application</h3>  
  <div class="messaging">  
    <div class="inbox_msg">  
      <div class="mesgs">  
        <div class="msg_history">  
          <div *ngFor="let msg of messages">  
          <div class="incoming_msg" *ngIf="msg.type == 'received'">  
            <div class="incoming_msg_img"> </div>  
            <div class="received_msg">  
              <div class="received_withd_msg">  
                <p>  
                 {{msg.message}}   
                </p>  
                <span class="time_date"> {{msg.date | date:'medium'}} </span>  
              </div>  
            </div>  
          </div>  
          <div class="outgoing_msg" *ngIf="msg.type == 'sent'">  
            <div class="sent_msg">  
              <p>  
                  {{msg.message}}   
              </p>  
              <span class="time_date"> {{msg.date | date:'medium'}}</span>  
            </div>  
          </div>  
        </div>  
        </div>  
        <div class="type_msg">  
          <div class="input_msg_write">  
            <input type="text" class="write_msg" [value]="txtMessage"  
            (input)="txtMessage=$event.target.value" (keydown.enter)="sendMessage()" placeholder="Type a message" />  
            <button class="msg_send_btn" type="button"  (click)="sendMessage()"><i class="fa fa-paper-plane-o" aria-hidden="true"></i></button>  
          </div>  
        </div>  
      </div>  
    </div>  
  
  </div>  
</div>  

app.component.ts :

import { Component, NgZone } from '@angular/core';  
import { Message } from '../models/Message';  
import { ChatService } from '../services/chat.service';  
  
@Component({  
  selector: 'app-root',  
  templateUrl: './app.component.html',  
  styleUrls: ['./app.component.css']  
})  
export class AppComponent {  
  
  title = 'ClientApp';  
  txtMessage: string = '';  
  uniqueID: string = new Date().getTime().toString();  
  messages = new Array<Message>();  
  message = new Message();  
  constructor(  
    private chatService: ChatService,  
    private _ngZone: NgZone  
  ) {  
    this.subscribeToEvents();  
  }  
  sendMessage(): void {  
    if (this.txtMessage) {  
      this.message = new Message();  
      this.message.clientuniqueid = this.uniqueID;  
      this.message.type = "sent";  
      this.message.message = this.txtMessage;  
      this.message.date = new Date();  
      this.messages.push(this.message);  
      this.chatService.sendMessage(this.message);  
      this.txtMessage = '';  
    }  
  }  
  private subscribeToEvents(): void {  
  
    this.chatService.messageReceived.subscribe((message: Message) => {  
      this._ngZone.run(() => {  
        if (message.clientuniqueid !== this.uniqueID) {  
          message.type = "received";  
          this.messages.push(message);  
        }  
      });  
    });  
  }  
}  

Now, our chat application is ready. Let's run the following command in terminal and test apps.

dotnet run  
Summary

In this article, we have learned how we can create a sample chat app using Angular 8 and ASP.NET Core with SignalR.

Please find the entire source code here as an attachment and also on GitHub.

Thanks!