How To Create To-Do CRUD Operation With ASP.NET MVC Core, Angular 4.0

We want to create below single page application with CRUD operation, Add, Update and Remove functionality with in-memory TodoRepository Database.

ASP.NET MVC Core

Introduction

In this session, we will learn,

  • How to consume Todo in-memory database using TodoRepository.
  • How to create custom ASP.NET  custom API controller with CRUD operations.
  • List of Objects
  • Create a new insert item
  • Update an Item
  • Delete an Item 
  • Write HttpPost Create API method
  • Write HttpPut Edit API method.
  • Write HttpPost Delete API method.
  • Loose Coupling
  • Dependency Injection
  • Singleton
  • Route
  • Responsive Web Design

A lab exercise for you to demonstrate what have you learned from this training material to create your own Employee CRUD operation using EmployeeRepository included in this training material.

Pre-Requirements

In order to be able to run the example from the download or build it from scratch, you need to have the following tools,

  • Visual Studio 2017 latest version
  • .NET Core 2.0 or above
  • TypeScript 1.8 or above
  • Node.js
  • Angular 4.0 or above

Create a new solution and name it TodoAspNetCoreMvcAngular4Vs2017Solution

ASP.NET MVC Core

Add a new ASP.NET Core Web Application project and name it TodoAngular.Ui

ASP.NET MVC Core

Visual  Studio 2017 ASP.NET Core 2.0 comes with an Angular project template.

Next screen select Angular Web Application project template.

ASP.NET MVC Core

Compile and run the application and we’ll see the home page.

ASP.NET MVC Core

Lets change to home page content

Go to the home.component.html in the ClientApp directory.

ASP.NET MVC Core

Replace the home content with below lines when the project is still running. Angular has change detection functionally and reflects  your  changes on the running application.

Home content will change on the run time.

ASP.NET MVC Core

<h1>To-do Angular 4.0 .NETCore CRUD Application </h1>
<p>Provided by -SmartIT,  John Kocer!</p>

We want to create below single page application with CRUD operation, Add, Update and Remove functionality with in-memory TodoRepository Database.

ASP.NET MVC Core

We need to do below tasks,

  • Set TodoRepository
  • Create TodoController
  • Add Dependency Injection
  • Create todoService.ts
  • Create todo.component.html
  • Create todo.component.ts
  • Add new created component into ApModule
  • Update Angular routing to go Todo page.
Set TodoRepository

Use SmartIT.Employee.MockDB which has TodoRepository in it with in-memory MOCK database which has dependency injection interfaces implemented . You may go to below web site and read the usage examples.

https://www.nuget.org/packages/SmartIT.Employee.MockDB/

Use Nuget Package manager to Install SmartIT.Employee.MockDB from nugget.org.

ASP.NET MVC Core

Create TodoController

On the controllers directory add a new controller select Full Dependencies and Add.

ASP.NET MVC Core

Select API Controler with read/write actions from the list and click Add

ASP.NET MVC Core

Name it TodoController and Click Add.

ASP.NET MVC Core

TodoController will be created.

  1. namespace TodoAngular.Ui.Controllers  
  2. {  
  3.     [Produces("application/json")]  
  4.     [Route("api/Todo")]  
  5.     public class TodoController : Controller  
  6.     {  
  7.         // GET: api/Todo  
  8.         [HttpGet]  
  9.         public IEnumerable<string> Get()  
  10.         {  
  11.             return new string[] { "value1""value2" };  
  12.         }  
  13.   
  14.         // GET: api/Todo/5  
  15.         [HttpGet("{id}", Name = "Get")]  
  16.         public string Get(int id)  
  17.         {  
  18.             return "value";  
  19.         }  
  20.           
  21.         // POST: api/Todo  
  22.         [HttpPost]  
  23.         public void Post([FromBody]string value)  
  24.         {  
  25.         }  
  26.           
  27.         // PUT: api/Todo/5  
  28.         [HttpPut("{id}")]  
  29.         public void Put(int id, [FromBody]string value)  
  30.         {  
  31.         }  
  32.           
  33.         // DELETE: api/ApiWithActions/5  
  34.         [HttpDelete("{id}")]  
  35.         public void Delete(int id)  
  36.         {  
  37.         }  
  38.     }  
  39. }  
Add Dependency Injection

Go to the Startup page ConfigureServices method

  1. public class Startup  
  2. {  
  3.     public Startup(IConfiguration configuration)  
  4.     {  
  5.         Configuration = configuration;  
  6.     }  
  7.   
  8.     public IConfiguration Configuration { get; }  
  9.   
  10.     // This method gets called by the runtime. Use this method to add services to the container.  
  11.     public void ConfigureServices(IServiceCollection services)  
  12.     {  
  13.         services.AddMvc();  
  14.     } 
Add the SmartIT.Employee.MockDB  using statement top of the Startup page.
  1. using SmartIT.Employee.MockDB;  

Add the AddSigleton service. Why we chose as Sigleton? Because our service is an In-memory repository data services. We want only a single instance will be created.

  1. services.AddSingleton<ITodoRepository, TodoRepository>(); //Add the services here  
  2.   
  3. using SmartIT.Employee.MockDB;  
  4.   
  5. namespace TodoAngular_Ui  
  6. {  
  7.   public class Startup  
  8.   {  
  9.     public Startup(IConfiguration configuration)  
  10.     {  
  11.       Configuration = configuration;  
  12.     }  
  13.   
  14.     public IConfiguration Configuration { get; }  
  15.   
  16.     // This method gets called by the runtime. Use this method to add services to the container.  
  17.     public void ConfigureServices(IServiceCollection services)  
  18.     {  
  19.       services.AddMvc();  
  20.       services.AddSingleton<ITodoRepository, TodoRepository>(); //Add the services here  
  21.     }  
Add top of the TodoController page

using SmartIT.Employee.MockDB;

Add private readonly ITodoRepository _todoRepository; readonly ItodoRepositor Interface member.

Add a Todo Constructor

  1. using SmartIT.Employee.MockDB;  
  2.   
  3. namespace TodoAngular.Ui.Controllers  
  4. {  
  5.   [Produces("application/json")]  
  6.   [Route("api/Todo")]  
  7.   public class TodoController : Controller  
  8.   {  
  9.     private readonly ITodoRepository _todoRepository;  
  10.     public TodoController(ITodoRepository todoRepository)  
  11.     {  
  12.       _todoRepository = todoRepository;  
  13.     }  
Add GetAllTodos Httpget mehot with [Route("~/api/GetAllTodos")]

Here is the final changes in below on the TodoController

  1. using SmartIT.Employee.MockDB;  
  2.   
  3. namespace TodoAngular.Ui.Controllers  
  4. {  
  5.   [Produces("application/json")]  
  6.   [Route("api/Todo")]  
  7.   public class TodoController : Controller  
  8.   {  
  9.     private readonly ITodoRepository _todoRepository;  
  10.     public TodoController(ITodoRepository todoRepository)  
  11.     {  
  12.       _todoRepository = todoRepository;  
  13.     }  
  14.     // GET: api/Todo  
  15.     [Route("~/api/GetAllTodos")]  
  16.     [HttpGet]  
  17.     public IEnumerable<Todo> GetAllTodos()  
  18.     {  
  19.       return _todoRepository.GetAll();  
  20.     }  
Compile and run the application.

Test get all todos with calling to API

http://localhost:61190/api/GetAllTodos

Note

Your port number may be different than ours, use your local port number.

ASP.NET MVC Core

Update the remainder of the TodoController like below.

  1. using System.Collections.Generic;  
  2. using Microsoft.AspNetCore.Mvc;  
  3. using SmartIT.Employee.MockDB;  
  4.   
  5. namespace TodoAngular.Ui.Controllers  
  6. {  
  7.   [Produces("application/json")]  
  8.   [Route("api/Todo")]  
  9.   public class TodoController : Controller  
  10.   {  
  11.     private readonly ITodoRepository _todoRepository;  
  12.     public TodoController(ITodoRepository todoRepository)  
  13.     {  
  14.       _todoRepository = todoRepository;  
  15.     }  
  16.   
  17.     [Route("~/api/GetAllTodos")]  
  18.     [HttpGet]  
  19.     public IEnumerable<Todo> GetAllTodos()  
  20.     {  
  21.       return _todoRepository.GetAll();  
  22.     }  
  23.   
  24.     [Route("~/api/AddTodo")]  
  25.     [HttpPost]  
  26.     public Todo AddTodo([FromBody]Todo item)  
  27.     {  
  28.       return _todoRepository.Add(item);  
  29.     }  
  30.   
  31.     [Route("~/api/UpdateTodo")]  
  32.     [HttpPut]  
  33.     public Todo UpdateTodo([FromBody]Todo item)  
  34.     {  
  35.       return  _todoRepository.Update(item);  
  36.     }  
  37.   
  38.     [Route("~/api/DeleteTodo/{id}")]  
  39.     [HttpDelete]  
  40.     public void Delete(int id)  
  41.     {  
  42.       var findTodo = _todoRepository.FindById(id);  
  43.       if (findTodo != null)  
  44.         _todoRepository.Delete(findTodo);  
  45.     }  
  46.   }  
  47. }  
Create todoService.ts

Add a Todo folder under components directory.

ASP.NET MVC Core

Add a TypeScript file todoService.ts into Todo directory.


ASP.NET MVC Core
yo

Paste the below code into your todoService.ts

  1. import { Injectable } from "@angular/core";  
  2. import { Http, Response, Headers } from "@angular/http";  
  3. import "rxjs/add/operator/map";  
  4. import 'rxjs/add/operator/do';  // debug  
  5. import { Observable } from "rxjs/Observable";  
  6. import { BehaviorSubject } from 'rxjs/BehaviorSubject';  
  7.   
  8. @Injectable()  
  9. export class TodoService {  
  10.   
  11.   public todoList: Observable<Todo[]>;  
  12.   private _todoList: BehaviorSubject<Todo[]>;  
  13.   private baseUrl: string;  
  14.   private dataStore: {  
  15.     todoList: Todo[];  
  16.   };  
  17.   
  18.   constructor(private http: Http) {  
  19.     this.baseUrl = '/api/';  
  20.     this.dataStore = { todoList: [] };  
  21.     this._todoList = <BehaviorSubject<Todo[]>>new BehaviorSubject([]);  
  22.     this.todoList = this._todoList.asObservable();  
  23.   }  
  24.   
  25.   getAll() {  
  26.     this.http.get(`${this.baseUrl}GetAllTodos`)  
  27.       .map(response => response.json())  
  28.       .subscribe(data => {  
  29.         this.dataStore.todoList = data;  
  30.         this._todoList.next(Object.assign({}, this.dataStore).todoList);  
  31.       }, error => console.log('Could not load todo.'));  
  32.   }  
  33.   public addTodo(newTodo: Todo) {  
  34.     console.log("addTodo");  
  35.     var headers = new Headers();  
  36.     headers.append('Content-Type''application/json; charset=utf-8');  
  37.     console.log('add todo : ' + JSON.stringify(newTodo));  
  38.   
  39.   
  40.     this.http.post(`${this.baseUrl}AddTodo/`, JSON.stringify(newTodo), { headers: headers })  
  41.       .map(response => response.json()).subscribe(data => {  
  42.         this.dataStore.todoList.push(data);  
  43.         this._todoList.next(Object.assign({}, this.dataStore).todoList);  
  44.       }, error => console.log('Could not create todo.'));  
  45.   };  
  46.   
  47.   public updateTodo(newTodo: Todo) {  
  48.     console.log("updateTodo");  
  49.     var headers = new Headers();  
  50.     headers.append('Content-Type''application/json; charset=utf-8');  
  51.     console.log('add todo : ' + JSON.stringify(newTodo));  
  52.   
  53.   
  54.     this.http.put(`${this.baseUrl}UpdateTodo/`, JSON.stringify(newTodo), { headers: headers })  
  55.       .map(response => response.json()).subscribe(data => {  
  56.         this.dataStore.todoList.forEach((t, i) => {  
  57.           if (t.id === data.id) { this.dataStore.todoList[i] = data; }  
  58.         });  
  59.       }, error => console.log('Could not update todo.'));  
  60.   };  
  61.   
  62.   removeItem(todoId: number) {  
  63.     var headers = new Headers();  
  64.     headers.append('Content-Type''application/json; charset=utf-8');  
  65.     console.log("removeItem:" + todoId);  
  66.     this.http.delete(`${this.baseUrl}DeleteTodo/${todoId}`, { headers: headers }).subscribe(response => {  
  67.       this.dataStore.todoList.forEach((t, i) => {  
  68.         if (t.id === todoId) { this.dataStore.todoList.splice(i, 1); }  
  69.       });  
  70.   
  71.       this._todoList.next(Object.assign({}, this.dataStore).todoList);  
  72.     }, error => console.log('Could not delete todo.'));  
  73.   }  
  74.   private _serverError(err: any) {  
  75.     console.log('sever errorOK:', err);  // debug  
  76.     if (err instanceof Response) {  
  77.       return Observable.throw(err.json().error || 'backend server error');  
  78.       // if you're using lite-server, use the following line  
  79.       // instead of the line above:  
  80.       //return Observable.throw(err.text() || 'backend server error');  
  81.     }  
  82.     return Observable.throw(err || 'backend server error');  
  83.   }  
  84. }  
  85.   
  86. export class Todo {  
  87.   public id: number;  
  88.   public name: string;  
  89. }  

Create todo.component.html

Add an HTML page into Todo directory and name it todo.component.html

ASP.NET MVC Core

Paste the below code in your todo.component.html

  1. <div id="summary" class="section panel panel-primary">  
  2.   <div class="panel-heading"> Todo Summary</div>  
  3.   <div class="container">  
  4.     <!--{{todoList | json}}-->  
  5.     <table class="table table-striped table-condensed" >  
  6.       <thead>  
  7.         <tr>  
  8.           <th>Id</th>  
  9.           <th>Name</th>  
  10.           <th></th>  
  11.         </tr>  
  12.         <tr>  
  13.           <th> </th>  
  14.           <th>  
  15.             <input id="newName" [(ngModel)]='newTodo.name' type="text" placeholder="newName" />  
  16.           </th>  
  17.           <th>  
  18.             <button class="btn btn-primary" (click)="addTodo(newTodo)">Add  </button>  
  19.         </tr>  
  20.       </thead>  
  21.       <tbody *ngFor="let item of todoList | async">  
  22.         <tr>  
  23.           <td>{{item.id}} </td>  
  24.           <td><input type="text" [(ngModel)]='item.name' /></td>  
  25.           <td>  
  26.             <button class="btn btn-xs btn-primary" (click)="updateTodo(item)">Update</button>  
  27.           </td>  
  28.           <td>  
  29.             <button class="btn btn-xs btn-primary" (click)="deleteTodo(item.id)">Remove</button>  
  30.         </tr>  
  31.       </tbody>  
  32.     </table>  
  33.   
  34.   </div>  
  35. </div>  
Create todo.component.ts

Add TypeScript file called to.component.ts  into Todo directory.

ASP.NET MVC Core

Paste below code into your to.component.ts .

  1. import { Component, OnInit } from '@angular/core';  
  2. import 'rxjs/add/operator/catch';  
  3. import { TodoService } from "./todoService";  
  4. import { Observable } from "rxjs/Observable";  
  5. import { Todo } from "./todoService"  
  6.   
  7. @Component({  
  8.   selector: 'todo',  
  9.   providers: [TodoService],  
  10.   templateUrl: './todo.component.html'  
  11. })  
  12. export class TodoComponent implements OnInit {  
  13.   public todoList: Observable<Todo[]>;  
  14.   
  15.   showEditor = true;  
  16.   myName: string;  
  17.   newTodo: Todo;  
  18.   constructor(private dataService: TodoService) {  
  19.     this.newTodo = new Todo();  
  20.   }  
  21.   // if you want to debug info  just uncomment the console.log lines.  
  22.   ngOnInit() {  
  23.     //    console.log("in ngOnInit");  
  24.     this.todoList = this.dataService.todoList;  
  25.     this.dataService.getAll();  
  26.   }  
  27.   public addTodo(item: Todo) {  
  28.     //console.dir(item);  
  29.     let todoId = this.dataService.addTodo(this.newTodo);  
  30.   }  
  31.   public updateTodo(item: Todo) {  
  32.     //  console.dir(item);  
  33.     //console.log("In updateTodo: " + item);  
  34.     this.dataService.updateTodo(item);  
  35.     //    console.log("in updateTodo:" );  
  36.   }  
  37.   public deleteTodo(todoId: number) {  
  38.     this.dataService.removeItem(todoId);  
  39.   }  
  40. }  
Add new created component into ApModule 
  1. import { CounterComponent } from './components/counter/counter.component';  
  2. import { TodoComponent } from './components/todo/todo.component';  
  3.   
  4. @NgModule({  
  5.   declarations: [  
  6.     AppComponent,  
  7.     NavMenuComponent,  
  8.     CounterComponent,  
  9.     FetchDataComponent,  
  10.     HomeComponent,  
  11.     TodoComponent  
  12.   ],  
  13.   imports: [  
  14.     CommonModule,  
  15.     HttpModule,  
  16.     FormsModule,  
  17.     RouterModule.forRoot([  
  18.       { path: '', redirectTo: 'home', pathMatch: 'full' },  
  19.       { path: 'todo', component: TodoComponent },  
  20.       { path: 'home', component: HomeComponent },  
  21.       { path: 'counter', component: CounterComponent },  
  22.       { path: 'fetch-data', component: FetchDataComponent },  
  23.       { path: '**', redirectTo: 'home' }  
  24.     ])  
  25.   ]  
  26. })  
  27. export class AppModuleShared { 

Lets update the menu

Change this line from <a class='navbar-brand' [routerLink]="['/home']">TodoAngular_Ui</a>

To <a class='navbar-brand' [routerLink]="['/home']">SmartIT by John Kocer</a>

Add new menu Item Todo

  1. <li [routerLinkActive]="['link-active']">  
  2.                     <a [routerLink]="['/todo]">  
  3.                         <span class='glyphicon glyphicon-user'></span> Todo  
  4.                     </a>  
  5.   </li>  

Here is updated menu

  1. <div class='main-nav'>  
  2.   <div class='navbar navbar-inverse'>  
  3.     <div class='navbar-header'>  
  4.       <button type='button' class='navbar-toggle' data-toggle='collapse' data-target='.navbar-collapse'>  
  5.         <span class='sr-only'>Toggle navigation</span>  
  6.         <span class='icon-bar'></span>  
  7.         <span class='icon-bar'></span>  
  8.         <span class='icon-bar'></span>  
  9.       </button>  
  10.       <a class='navbar-brand' [routerLink]="['/home']">SmartIT by John Kocer</a>  
  11.     </div>  
  12.     <div class='clearfix'></div>  
  13.     <div class='navbar-collapse collapse'>  
  14.       <ul class='nav navbar-nav'>  
  15.         <li [routerLinkActive]="['link-active']">  
  16.           <a [routerLink]="['/todo']">  
  17.             <span class='glyphicon glyphicon-user'></span> Todo  
  18.           </a>  
  19.         </li>  
  20.   
  21.         <li [routerLinkActive]="['link-active']">  
  22.           <a [routerLink]="['/home']">  
  23.             <span class='glyphicon glyphicon-home'></span> Home  
  24.           </a>  
  25.         </li>  
  26.         <li [routerLinkActive]="['link-active']">  
  27.           <a [routerLink]="['/counter']">  
  28.             <span class='glyphicon glyphicon-education'></span> Counter  
  29.           </a>  
  30.         </li>  
  31.         <li [routerLinkActive]="['link-active']">  
  32.           <a [routerLink]="['/fetch-data']">  
  33.             <span class='glyphicon glyphicon-th-list'></span> Fetch data  
  34.           </a>  
  35.         </li>  
  36.       </ul>  
  37.     </div>  
  38.   </div>  
  39. </div>  
Update the RouteModule
  1. RouterModule.forRoot([  
  2.       { path: '', redirectTo: 'home', pathMatch: 'full' },  
  3.       { path: 'todo', component: TodoComponent },  
  4.       { path: 'home', component: HomeComponent },  
  5.       { path: 'counter', component: CounterComponent },  
  6.       { path: 'fetch-data', component: FetchDataComponent },  
  7.       { path: '**', redirectTo: 'home' }  
  8.     ])  

Here is the updated RouteModule

  1. import { NgModule } from '@angular/core';  
  2. import { CommonModule } from '@angular/common';  
  3. import { FormsModule } from '@angular/forms';  
  4. import { HttpModule } from '@angular/http';  
  5. import { RouterModule } from '@angular/router';  
  6.   
  7. import { AppComponent } from './components/app/app.component';  
  8. import { NavMenuComponent } from './components/navmenu/navmenu.component';  
  9. import { HomeComponent } from './components/home/home.component';  
  10. import { FetchDataComponent } from './components/fetchdata/fetchdata.component';  
  11. import { CounterComponent } from './components/counter/counter.component';  
  12. import { TodoComponent } from './components/todo/todo.component';  
  13.   
  14. @NgModule({  
  15.   declarations: [  
  16.     AppComponent,  
  17.     NavMenuComponent,  
  18.     CounterComponent,  
  19.     FetchDataComponent,  
  20.     HomeComponent,  
  21.     TodoComponent  
  22.   ],  
  23.   imports: [  
  24.     CommonModule,  
  25.     HttpModule,  
  26.     FormsModule,  
  27.     RouterModule.forRoot([  
  28.       { path: '', redirectTo: 'todo', pathMatch: 'full' },  
  29.       { path: 'todo', component: TodoComponent },  
  30.       { path: 'home', component: HomeComponent },  
  31.       { path: 'counter', component: CounterComponent },  
  32.       { path: 'fetch-data', component: FetchDataComponent },  
  33.       { path: '**', redirectTo: 'todo' }  
  34.     ])  
  35.   ]  
  36. })  
  37. export class AppModuleShared {  

Here is the final result of responsive web design result.

ASP.NET MVC Core

Summary

In this article, we will learned,

  • How to consume Todo in-memory database using TodoRepository.
  • How to create custom ASP.NET MVC custom controller with CRUD operations.
  • List of Objects
  • Create a new insert object 
  • Update an item
  • Delete an Item 
  • Write HttpPost Create API method.
  • Write HttpPut Edit API method.
  • Write HttpPost Delete API method.
  • Loose Coupling
  • Dependency Injection
  • Singleton
  • Route
  • Responsive Web Design

Lab Exercise

A lab exercise for you to demonstrate what have you learned from this training material to create your own Employee Angular CRUD Responsive Design SPA application. The EmployeeRepository included in this training material.

You can follow above steps to create your own Employee CRUD ASP.NET MVC Angular 4.0 application.

  1.  //Use below Employee repository to created your CRUD operation  
  2.   EmployeeRepository _employeeRepository= new EmployeeRepository();  
  3.   _employeeRepository.Add(new Employee() { Name = "Mat Stone", Gender = "Male", DepartmentId = 2, Salary = 8000 });  
  4.   _employeeRepository.CDump("Employees");  
  5. //  DebugHelper.cs(29): Employees  
  6. //{ "Items":[{"Id":1,"Name":"Mike","Gender":"Male","Salary":8000,"DepartmentId":1,"Department":null},  
  7. //{"Id":2,"Name":"Adam","Gender":"Male","Salary":5000,"DepartmentId":1,"Department":null},  
  8. //{"Id":3,"Name":"Jacky","Gender":"Female","Salary":9000,"DepartmentId":1,"Department":null},  
  9. //{"Id":4,"Name":"Mat Stone","Gender":"Male","Salary":8000,"DepartmentId":2,"Department":null}],"Count":4} 

For ASP.NET MVC Core Angular 4 CRUD Application

NOTE


If you need to copy and paste the code download the pdf file locally.

Download source code from GitHub.