Nico Jonsson

Nico Jonsson

1578402208

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. 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.

This is image title

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)

This is image title

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.

This is image title

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.

This is image title

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

This is image title

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.

This is image title

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.

This is image title

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.

This is image title

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 #asp-net #cosmos-db #signair

What is GEEK

Buddha Community

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

Jessica Smith

1612606870

REAL TIME CHAT SOLUTIONS SERVICES FOR MOBILE APPS

Build a Real Time chat application that can integrated into your social handles. Add more life to your website or support portal with a real time chat solutions for mobile apps that shows online presence indicators, typing status, timestamp, multimedia sharing and much more. Users can also log into the live chat app using their social media logins sparing them from the need to remember usernames and passwords. For more information call us at +18444455767 or email us at hello@sisgain.com or Visit: https://sisgain.com/instant-real-time-chat-solutions-mobile-apps

#real time chat solutions for mobile apps #real time chat app development solutions #live chat software for mobile #live chat software solutions #real time chat app development #real time chat applications in java script

Ian  Robinson

Ian Robinson

1621644000

4 Real-Time Data Analytics Predictions for 2021

Data management, analytics, data science, and real-time systems will converge this year enabling new automated and self-learning solutions for real-time business operations.

The global pandemic of 2020 has upended social behaviors and business operations. Working from home is the new normal for many, and technology has accelerated and opened new lines of business. Retail and travel have been hit hard, and tech-savvy companies are reinventing e-commerce and in-store channels to survive and thrive. In biotech, pharma, and healthcare, analytics command centers have become the center of operations, much like network operation centers in transport and logistics during pre-COVID times.

While data management and analytics have been critical to strategy and growth over the last decade, COVID-19 has propelled these functions into the center of business operations. Data science and analytics have become a focal point for business leaders to make critical decisions like how to adapt business in this new order of supply and demand and forecast what lies ahead.

In the next year, I anticipate a convergence of data, analytics, integration, and DevOps to create an environment for rapid development of AI-infused applications to address business challenges and opportunities. We will see a proliferation of API-led microservices developer environments for real-time data integration, and the emergence of data hubs as a bridge between at-rest and in-motion data assets, and event-enabled analytics with deeper collaboration between data scientists, DevOps, and ModelOps developers. From this, an ML engineer persona will emerge.

#analytics #artificial intelligence technologies #big data #big data analysis tools #from our experts #machine learning #real-time decisions #real-time analytics #real-time data #real-time data analytics

Roberta  Ward

Roberta Ward

1595344320

Wondering how to upgrade your skills in the pandemic? Here's a simple way you can do it.

Corona Virus Pandemic has brought the world to a standstill.

Countries are on a major lockdown. Schools, colleges, theatres, gym, clubs, and all other public places are shut down, the country’s economy is suffering, human health is on stake, people are losing their jobs and nobody knows how worse it can get.

Since most of the places are on lockdown, and you are working from home or have enough time to nourish your skills, then you should use this time wisely! We always complain that we want some ‘time’ to learn and upgrade our knowledge but don’t get it due to our ‘busy schedules’. So, now is the time to make a ‘list of skills’ and learn and upgrade your skills at home!

And for the technology-loving people like us, Knoldus Techhub has already helped us a lot in doing it in a short span of time!

If you are still not aware of it, don’t worry as Georgia Byng has well said,

“No time is better than the present”

– Georgia Byng, a British children’s writer, illustrator, actress and film producer.

No matter if you are a developer (be it front-end or back-end) or a data scientisttester, or a DevOps person, or, a learner who has a keen interest in technology, Knoldus Techhub has brought it all for you under one common roof.

From technologies like Scala, spark, elastic-search to angular, go, machine learning, it has a total of 20 technologies with some recently added ones i.e. DAML, test automation, snowflake, and ionic.

How to upgrade your skills?

Every technology in Tech-hub has n number of templates. Once you click on any specific technology you’ll be able to see all the templates of that technology. Since these templates are downloadable, you need to provide your email to get the template downloadable link in your mail.

These templates helps you learn the practical implementation of a topic with so much of ease. Using these templates you can learn and kick-start your development in no time.

Apart from your learning, there are some out of the box templates, that can help provide the solution to your business problem that has all the basic dependencies/ implementations already plugged in. Tech hub names these templates as xlr8rs (pronounced as accelerators).

xlr8rs make your development real fast by just adding your core business logic to the template.

If you are looking for a template that’s not available, you can also request a template may be for learning or requesting for a solution to your business problem and tech-hub will connect with you to provide you the solution. Isn’t this helpful 🙂

Confused with which technology to start with?

To keep you updated, the Knoldus tech hub provides you with the information on the most trending technology and the most downloaded templates at present. This you’ll be informed and learn the one that’s most trending.

Since we believe:

“There’s always a scope of improvement“

If you still feel like it isn’t helping you in learning and development, you can provide your feedback in the feedback section in the bottom right corner of the website.

#ai #akka #akka-http #akka-streams #amazon ec2 #angular 6 #angular 9 #angular material #apache flink #apache kafka #apache spark #api testing #artificial intelligence #aws #aws services #big data and fast data #blockchain #css #daml #devops #elasticsearch #flink #functional programming #future #grpc #html #hybrid application development #ionic framework #java #java11 #kubernetes #lagom #microservices #ml # ai and data engineering #mlflow #mlops #mobile development #mongodb #non-blocking #nosql #play #play 2.4.x #play framework #python #react #reactive application #reactive architecture #reactive programming #rust #scala #scalatest #slick #software #spark #spring boot #sql #streaming #tech blogs #testing #user interface (ui) #web #web application #web designing #angular #coronavirus #daml #development #devops #elasticsearch #golang #ionic #java #kafka #knoldus #lagom #learn #machine learning #ml #pandemic #play framework #scala #skills #snowflake #spark streaming #techhub #technology #test automation #time management #upgrade

Christa  Stehr

Christa Stehr

1598940617

Install Angular - Angular Environment Setup Process

Angular is a TypeScript based framework that works in synchronization with HTML, CSS, and JavaScript. To work with angular, domain knowledge of these 3 is required.

  1. Installing Node.js and npm
  2. Installing Angular CLI
  3. Creating workspace
  4. Deploying your First App

In this article, you will get to know about the Angular Environment setup process. After reading this article, you will be able to install, setup, create, and launch your own application in Angular. So let’s start!!!

Angular environment setup

Install Angular in Easy Steps

For Installing Angular on your Machine, there are 2 prerequisites:

  • Node.js
  • npm Package Manager
Node.js

First you need to have Node.js installed as Angular require current, active LTS or maintenance LTS version of Node.js

Download and Install Node.js version suitable for your machine’s operating system.

Npm Package Manager

Angular, Angular CLI and Angular applications are dependent on npm packages. By installing Node.js, you have automatically installed the npm Package manager which will be the base for installing angular in your system. To check the presence of npm client and Angular version check of npm client, run this command:

  1. npm -v

Installing Angular CLI

  • Open Terminal/Command Prompt
  • To install Angular CLI, run the below command:
  1. npm install -g @angular/cli

installing angular CLI

· After executing the command, Angular CLI will get installed within some time. You can check it using the following command

  1. ng --version

Workspace Creation

Now as your Angular CLI is installed, you need to create a workspace to work upon your application. Methods for it are:

  • Using CLI
  • Using Visual Studio Code
1. Using CLI

To create a workspace:

  • Navigate to the desired directory where you want to create your workspace using cd command in the Terminal/Command prompt
  • Then in the directory write this command on your terminal and provide the name of the app which you want to create. In my case I have mentioned DataFlair:
  1. Ng new YourAppName

create angular workspace

  • After running this command, it will prompt you to select from various options about the CSS and other functionalities.

angular CSS options

  • To leave everything to default, simply press the Enter or the Return key.

angular setup

#angular tutorials #angular cli install #angular environment setup #angular version check #download angular #install angular #install angular cli

Clara  Gutmann

Clara Gutmann

1598716260

Angular 8 CRUD Example | Angular 8 Tutorial For Beginners

Angular 8 CRUD is a basic operation to learn Angular from scratch. We will learn how to build a small web application that inserts, read data, update and delete data from the database. You will learn how to create a MEAN Stack web application. In this Angular 8 Tutorial Example, you will learn a new framework by building a crud application.

New features of Angular 8

You check out the new features in brief on my  Angular 8 New Features post.

I have designed this Angular 8 CRUD Tutorial, especially for newcomers, and it will help you to up and running with the latest version of Angular, which is right now 8.

#angular #angular 8 #angular 8 crud