How to Create CRUD operation using Angular and ASP.NET Core Web API

In this article, we will learn How to Create CRUD operation using Angular and ASP.NET Core Web API. To demonstrate the topic, we will build a project from scratch.

We will take a systematic step-by-step approach, applying the principles of clean architecture, to develop CRUD operation using Angular and ASP.NET Core Web API

Prerequisites

ASP.NET Web API Project

Create Web API

dotnet new project_Name

Create Classlib project

# Create Domain layer
dotnet new classlib -n Domain
# Create Application layer
dotnet new classlib -n Application
# Create Infrastructure layer
dotnet new classlib -n Infrastructure

Install Packages

Install packages in the Infrastructure project

  • Microsoft.EntityFrameworkCore
  • Microsoft.EntityFrameworkCore.Design
  • Microsoft.EntityFrameworkCore.Sqlite
  • Microsoft.EntityFrameworkCore.Tools

Reference the projects

Now that we understand the principles of Clean Architecture, let’s see how we can reference it in an ASP.NET Core Web API project

Application Layer

  <ItemGroup>
    <ProjectReference Include="..\Domain\Domain.csproj" />
  </ItemGroup>

Infrastructure Layer

   <ItemGroup>
    <ProjectReference Include="..\Domain\Domain.csproj" />
    <ProjectReference Include="..\Application\Application.csproj" />
  </ItemGroup>

Web API

 <ItemGroup>
    <ProjectReference Include="..\Infrastructure\Infrastructure.csproj" />
  </ItemGroup>

Create Entities in the Domain Layer

path:/Domain/Entities

Employee.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Domain.Entities
{
    public class Employee : BaseEntity
    {        
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Street {get;set;}
        public string City { get; set; }
        public string State { get; set; }  
    }
}

BaseEntity.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Domain.Entities
{
    public class BaseEntity
    {
        public BaseEntity()
        {
            
            CreateAt = DateTime.UtcNow;
        }
        public int Id { get; set; }
        public DateTime CreateAt { get; set; }
    }
}

Create an Interface in the Application Layer

path:/Application/Interfaces

IEmployeeRepo.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Domain.Entities;
 
namespace Application.Interfaces
{
    public interface IEmployeeRepo
    {
        Task<IReadOnlyList<Employee>> GetEmployeeListAsync();
        Employee GetEmployeeById(int id);
        Employee CreateEmployee(Employee employee);
        Employee UpdateEmployee(Employee employee);
        void DeleteEmployee(int id);

    }
}

Create DB Context, Concrete Class, and Services in the Infrastructure Layer

path:/Infrastructure/Data

AppDbContext.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Domain.Entities;
using Microsoft.EntityFrameworkCore;

namespace Infrastructure.Data
{
    public class AppDbContext : DbContext
    {

        public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
        {
        }
        public DbSet<Employee> Employees { get; set;}
    }
}

path:/Infrastructure/Repository

EmployeeRepository.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Application.Interfaces;
using Domain.Entities;
using Infrastructure.Data;
using Microsoft.EntityFrameworkCore;

namespace Infrastructure.Repository
{
    public class EmployeeRepository : IEmployeeRepo
    {

        private readonly AppDbContext _context;

        public EmployeeRepository(AppDbContext context)
        {
            _context = context;
        }
        public Employee CreateEmployee(Employee employees)
        {
            _context.Employees.Add(employees);
            var result = _context.SaveChangesAsync();
            if (result.IsCompletedSuccessfully) { return employees; }

            return null;
        }

        public Employee GetEmployeeById(int id)
        {
            return _context.Employees.FindAsync(id).Result;
        }

        public async Task<IReadOnlyList<Employee>> GetEmployeeListAsync()
        {
            return await _context.Employees.ToListAsync();

        }

        public Employee UpdateEmployee(Employee employee)
        {
            this._context.Employees.Update(employee);
            _context.SaveChanges();
            return employee;
        }

        public void DeleteEmployee(int id)
        {
            var result = GetEmployeeById(id);
            _context.Employees.Remove(result);
            _context.SaveChanges();

        }
    }
}

path:/Infrastructure/Services

ServicesConfig.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Application.Interfaces;
using Infrastructure.Data;
using Infrastructure.Repository;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace Infrastructure.Services
{
    public static class ServicesConfig
    {
        public static IServiceCollection AddApplication(this IServiceCollection services, IConfiguration config)
        {

            services.AddDbContext<AppDbContext>(opt =>
            {
                opt.UseSqlite(config.GetConnectionString("connection"));
            });

            services.AddScoped<IEmployeeRepo, EmployeeRepository>();

           
            return services;
        }
    }
}

Update Program.cs

using Infrastructure.Services;
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

builder.Services.AddApplication(builder.Configuration);
//builder.Services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());

builder.Services.AddCors(Opt => Opt.AddDefaultPolicy(opt =>
                {
                    opt.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin();
                }));

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();


app.UseAuthorization();

app.MapControllers();
app.UseCors();
app.Run();

Migration

dotnet ef migrations add  initialcreate -p .\Infrastructure\ -s API  -o Migrations

dotnet ef database update -p Infrastructure -s API

Create Account Controller

EmployeeController.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Application.Interfaces;
using Domain.Entities;
using Microsoft.AspNetCore.Mvc;

namespace API.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class EmployeeController : ControllerBase
    {
        private readonly IEmployeeRepo _employeeRepo;


        public EmployeeController(IEmployeeRepo employeeRepo)
        {
            _employeeRepo = employeeRepo;

        }
        [HttpGet]
        public async Task<IReadOnlyList<Employee>> GetEmployees()
        {

            return await this._employeeRepo.GetEmployeeListAsync();
        }
        [HttpPost]
        public ActionResult EmployeePost(Employee employees)
        {

            var result = _employeeRepo.CreateEmployee(employees);
            return Ok(result);
        }

        [HttpPut]
        public ActionResult EmployeePut(Employee employee)
        {


            var result = _employeeRepo.UpdateEmployee(employee);
            return Ok(result);
        }

        [HttpGet("{id}")]
        public Employee EmployeeGetById(int id)
        {
            var result = this._employeeRepo.GetEmployeeById(id);
            return result;
        }
        [HttpDelete("{id}")]
        public ActionResult DeleteEmployee(int id)
        {
            this._employeeRepo.DeleteEmployee(id);
            return Ok("Employee Deleted");
        }
    }
}

Create Client-side Angular Project

To create the client-side application, we’ll be using Angular. This section will guide you through setting up an Angular project and creating the necessary components.

# Create a client side Application
ng new project_name
# Navigate the inside project folder
cd project_name

# open the VS Code
code .

# Run the App
ng serve

Create Module | Services | Routing | Shared Module

# Create a module
ng   g  m employee/employee --flat

# Create a Routing
ng   g  m employee/employee-routing --flat

# Create a Service
 ng   g  s employee/employee  --flat
# Shared module
Employee.ts

Update Routing

import { NgModule, createComponent } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Routes, RouterModule   } from '@angular/router';
import { ReadComponent } from './read/read.component';
import { UpdateComponent } from './update/update.component';
import { DeleteComponent } from './delete/delete.component';
import { CreateComponent } from './create/create.component';


const routes : Routes=[

  {path:'', component:ReadComponent},
  {path:'Create', component:CreateComponent}, 
  {path:'update/:id', component:UpdateComponent},
  {path:'delete/:id', component:DeleteComponent},

]

@NgModule({
  declarations: [],
  imports: [
    RouterModule.forChild(routes),
    CommonModule
  ],
  exports:[
    RouterModule
  ]
})
export class EmployeeRoutingModule { }

Update Module

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { CreateComponent } from './create/create.component';
import { UpdateComponent } from './update/update.component';
import { ReadComponent } from './read/read.component';
import { DeleteComponent } from './delete/delete.component';
import { EmployeeRoutingModule } from './employee-routing.module';
import { ReactiveFormsModule } from '@angular/forms';



@NgModule({
  declarations: [
    CreateComponent,
    UpdateComponent,
    ReadComponent,
    DeleteComponent
  ],
  imports: [
    EmployeeRoutingModule,
    ReactiveFormsModule,
    CommonModule
  ]
})
export class EmployeeModule { }

Create Shared Module

Employee.ts

export interface Employee {
    id : number,
    firstName : string,
    lastName : string,
    street : string,
    city : string,
    state : string,
    createAt : Date
}

Update Service

import { Injectable } from '@angular/core';
import { Router, Routes } from '@angular/router';
import { HttpClient } from '@angular/common/http'
import { Employee } from '../shared/modules/Employee';
import { map } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class EmployeeService {
  baseUrl = 'https://localhost:8080/api';
   
  constructor(private http : HttpClient ,private router : Router ) { }

  CreateEmployee(values:any){
      return this.http.post<Employee>(this.baseUrl + '/Employee/',values).pipe(
        map(employee=> {
          alert("Employee Created Successfully...!" + employee);
        })
      )
  }

  GetEmployee(){
    return this.http.get<Employee>(this.baseUrl + '/Employee/')
  }

  GetEmployeeById(id:number){
    return this.http.get<Employee>(this.baseUrl + '/Employee/'+ id)
  }

  UpdateEmployee(values:any){

    return this.http.put<Employee>(this.baseUrl + '/Employee/', values).pipe(
      map(emp=>{
        alert("Employee Updated Successfully...!" + emp);
      })
    )
  }
  RemoveEmployee(id:number){
    return this.http.delete(this.baseUrl + '/Employee/' + id);

  }
}

Create Component

# Create a component
ng  g c Employee/Read --skip-tests
ng  g c Employee/Create--skip-tests
ng  g c Employee/Update --skip-tests
ng  g c Employee/Delete --skip-tests

Update Read Component

read.component.ts

import { Component, OnInit } from '@angular/core';
import { EmployeeService } from '../employee.service';
import { ActivatedRoute } from '@angular/router';
import { Employee } from 'src/app/shared/modules/Employee';

@Component({
  selector: 'app-read',
  templateUrl: './read.component.html',
  styleUrls: ['./read.component.scss']
})
export class ReadComponent implements OnInit {
  employee? :any;

 constructor(private employeeServices: EmployeeService, private router:ActivatedRoute){}

  ngOnInit(): void {
    this.loadEmployee();
  }

  loadEmployee(){
    this.employeeServices.GetEmployee().subscribe({
       next : emp => this.employee=emp,
       error: error=> alert(error)       
    });
  }
}

read.component.html

<div>
<table class="table table-success  table-striped">
<thead>
    <tr>
    <th>Id</th>
    <th>First Name</th>
    <th>Last Name</th>
    <th>Street</th>
    <th>City</th>
    <th>State</th>
    <th>Created At</th>
    <th>Action</th>
    </tr>
</thead>
<tbody *ngFor="let emp of employee">
    <tr>
        <td>{{emp.id}}</td>
        <td>{{emp.firstName}}</td>
        <td>{{emp.lastName}}</td>
        <td>{{emp.street}}</td>
        <td>{{emp.city}}</td>
        <td>{{emp.state}}</td>
        <td>{{emp.createAt}}</td>
        <td>
            <h5 class="mb-0">
                <a routerLink="/delete/{{emp.id}}" class="text-dark text-decoration-none">
                    <span class="fa fa-trash"></span> 
                </a>
            </h5>
        
            <h5 class="mb-0">
                <a routerLink="/update/{{emp.id}}" class="text-dark text-decoration-none">
            <span class="fa fa-edit"></span>
                   
                </a>
            </h5>
        </td>
    </tr>
</table>
</div>

Update Create Component

create.component.ts

import { Component } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { EmployeeService } from '../employee.service';
import { Router } from '@angular/router';

@Component({
  selector: 'app-create',
  templateUrl: './create.component.html',
  styleUrls: ['./create.component.scss']
})
export class CreateComponent {
  title : string ='Employee Registeration Form';

  EmpForm = new FormGroup({
    firstName : new FormControl('',Validators.required),
    lastName: new FormControl('',Validators.required),
    street: new FormControl('',Validators.required),
    city: new FormControl('',Validators.required),
    state: new FormControl('',Validators.required) 
  });

   constructor(private employeeService: EmployeeService, private router: Router){}

  onSubmit(){
    this.employeeService.CreateEmployee(this.EmpForm.value).subscribe({
      next:()=> this.router.navigateByUrl('/'),
      error:()=> alert("Please check ...!") 
    })
    
  }
}

create.component.html

<div class="d-flex justify-content-center mt-5">
    <div class="col-3">
        <form [formGroup]="EmpForm"  (ngSubmit)="onSubmit()">
            <div class="text-center mb-4">
                <h1 class="mb-3">
                    Employee
                </h1>
            </div>
            <div class="form-floating mb-3">
                <input type="firstName" formControlName="firstName"  class="form-control" id="floatingInput" placeholder="first Name">
                <label for="floatingInput">firstName</label>
              </div>
              <div class="form-floating">
                <input type="lastName" formControlName="lastName" class="form-control" id="floatingPassword" placeholder="last Name">
                <label for="floatingPassword">lastName</label>
              </div>
              <div class="form-floating">
                <input type="street" formControlName="street" class="form-control" id="floatingPassword" placeholder="street">
                <label for="floatingPassword">street</label>
              </div>
              <div class="form-floating">
                <input type="city" formControlName="city" class="form-control" id="floatingPassword" placeholder="city">
                <label for="floatingPassword">city</label>
              </div>
              <div class="form-floating">
                <input type="state" formControlName="state" class="form-control" id="floatingPassword" placeholder="state">
                <label for="floatingPassword">state</label>
              </div>
              <div class="d-grid">
                <button class="btn btn-lg btn-primary mt-3" type="submit">
                     Submit
                </button>
              </div>
        </form>
    </div>
</div>

Update Edit Component

update.component.ts

import { Component, OnInit } from '@angular/core';
import { EmployeeService } from '../employee.service';
import { ActivatedRoute, Router } from '@angular/router';
import { FormControl, FormGroup, Validators } from '@angular/forms';

@Component({
  selector: 'app-update',
  templateUrl: './update.component.html',
  styleUrls: ['./update.component.scss']
})
export class UpdateComponent implements OnInit{
employee?:any;
id?:any; 



constructor(private employeeService:EmployeeService, private router:ActivatedRoute,private route:Router)
{
  this.id = this.router.snapshot.paramMap.get('id');
}


EmpForm = new FormGroup({
  firstName : new FormControl('',Validators.required),
  lastName: new FormControl('',Validators.required),
  street: new FormControl('',Validators.required),
  city: new FormControl('',Validators.required),
  state: new FormControl('',Validators.required),
  id: new FormControl('',Validators.required) 
});
  ngOnInit(): void {
     this.getEmployee();
  }
getEmployee(){

  if(this.id) this.employeeService.GetEmployeeById(this.id).subscribe({
    next : emp => this.employee = emp,
    error : error=>alert(error)
  });
}
onSubmit(){
   this.employeeService.UpdateEmployee(this.EmpForm.value).subscribe({
    next:()=>this.route.navigateByUrl('/'),
    error:()=>alert("Please check...!")
   })
}
}

update.component.html

<p>update works!</p>
<div class="d-flex justify-content-center mt-10">
    <div class="col-10">
        
        <div >
            <form  *ngIf="employee" [formGroup]="EmpForm" (ngSubmit)="onSubmit()">
            <fieldset>
              

              <div class="mb-3">
                <label for="disabledTextInput" class="form-label">First Name</label>
                <input type="text" id="firstName"  formControlName="firstName"  class="form-control" [(ngModel)]="employee.firstName" >
              </div>
              
              <div class="mb-3">
                <label for="disabledTextInput" class="form-label">Last Name</label>
                <input type="text" id="lastName"  formControlName="lastName"  class="form-control"  [(ngModel)]="employee.lastName">
              </div>

              <div class="mb-3">
                <label for="disabledTextInput" class="form-label">Street</label>
                <input type="text" id="street"  formControlName="street"  class="form-control"  [(ngModel)]="employee.street">
              </div>

              <div class="mb-3">
                <label for="disabledTextInput" class="form-label">City</label>
                <input type="text" id="city" formControlName="city" class="form-control"  [(ngModel)]="employee.city">
              </div>

              <div class="mb-3">
                <label for="disabledTextInput" class="form-label">State</label>
                <input type="text" id="state" formControlName="state"  class="form-control"  [(ngModel)]="employee.state">
              </div>
              
              <input type="hidden" id="id"  formControlName="id"  class="form-control" [(ngModel)]="employee.id" >
             <div class="d-grid">
                <button class="btn btn-lg btn-primary mt-3" type="submit">
                     Submit
                </button>
              </div>   
            </fieldset>
        </form> 
    </div> 

Update Delete Component

delete.component.ts

import { Component, OnInit } from '@angular/core';
import { EmployeeService } from '../employee.service';
import { ActivatedRoute, Router } from '@angular/router';

@Component({
  selector: 'app-delete',
  templateUrl: './delete.component.html',
  styleUrls: ['./delete.component.scss']
})
export class DeleteComponent implements OnInit {
employee?:any;
id?:any; 

constructor(private employeeService:EmployeeService, private router : ActivatedRoute, private route:Router)
{ 
    this.id =this.router.snapshot.paramMap.get('id');
} 
  ngOnInit(): void {    
    this.getEmployee();
  }
  getEmployee(){
   
    if(this.id) this.employeeService.GetEmployeeById(this.id).subscribe({
      next : emp => this.employee = emp,
      error : error=>alert(error)
    });
  }
  onSubmit(){   
      this.employeeService.RemoveEmployee(this.id).subscribe({
      next : ()=>this.route.navigateByUrl('/'),
      error :()=>alert("Please check..!")
     
    });
  } 
}

delete.component.html

<div class="d-flex justify-content-center mt-10">
    <div class="col-10">
        
        <div *ngIf="employee"><form>
            <fieldset disabled>
              
              <div class="mb-3">
                <label for="disabledTextInput" class="form-label">First Name</label>
                <input type="text" id="" class="form-control" placeholder="{{ employee.firstName }}">
              </div>
              
              <div class="mb-3">
                <label for="disabledTextInput" class="form-label">Last Name</label>
                <input type="text" id="" class="form-control" placeholder="{{ employee.lastName }}">
              </div>

              <div class="mb-3">
                <label for="disabledTextInput" class="form-label">Street</label>
                <input type="text" id="" class="form-control" placeholder="{{ employee.street }}">
              </div>

              <div class="mb-3">
                <label for="disabledTextInput" class="form-label">City</label>
                <input type="text" id="" class="form-control" placeholder="{{ employee.city }}">
              </div>

              <div class="mb-3">
                <label for="disabledTextInput" class="form-label">State</label>
                <input type="text" id="" class="form-control" placeholder="{{ employee.state }}">
              </div>  
            </fieldset>
        </form>
            <div class="d-grid">
                <button class="btn btn-lg btn-primary mt-3" type="submit" (click)="onSubmit()">
                     Submit
                </button>
              </div> 
    </div> 

Root App Updating

Update app-routing. module

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

const routes: Routes = [
  {path:'',loadChildren:()=>import('./Employee/employee.module').then(emp=>emp.EmployeeModule)},
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Update app.component.html

  
  <link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css'>
 
<div class="d-flex justify-content-center mt-5">
   
    <div class="col-0">
        <h5 class="mb-0">
            <a routerLink="/Create/" class="text-dark text-decoration-none">
                <button class="btn btn-primary">Add Employee</button> 
            </a>
        </h5>
       
    <br>
<router-outlet></router-outlet>
    </div>
</div>

Related Article – How to Create a Complete Login Page using Angular and .NET Core

Get List

Update record

Delete Record

2.3 3 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments