Introduction
In this session, we will learn
- How to consume Todo in-memory database using TodoRepository.
- How to create a 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 the HttpPut Edit API method.
- Write the 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.
Prerequirements
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
Create a new solution and name it TodoAspNetCoreMvcAngular4Vs2017Solution
Add a new ASP.NET Core Web Application project and name it TodoAngular.Ui
Visual Studio 2017 ASP.NET Core 2.0 comes with an Angular project template.
Next screen select Angular Web Application project template.
Compile and run the application and we’ll see the home page.
Update home page content
Go to the home.component.html in the ClientApp directory.
Replace the home content with the 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.
<h1>To-do Angular 4.0 .NETCore CRUD Application </h1>
<p>Provided by -SmartIT, John Kocer!</p>
Tasks overview
We want to create the below single-page application with CRUD operation, Add, Update, and Remove functionality with the in-memory TodoRepository Database.
We need to do the 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 todo repository
Use SmartIT.Employee.MockDB which has TodoRepository in it with an in-memory MOCK database which has dependency injection interfaces implemented. You may go to the 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.
Create todo controller
On the controllers directory add a new controller select Full Dependencies and Add.
Select API Controler with read/write actions from the list and click Add
Name it TodoController and Click Add.
TodoController will be created.
namespace TodoAngular.Ui.Controllers
{
[Produces("application/json")]
[Route("api/Todo")]
public class TodoController : Controller
{
// GET: api/Todo
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET: api/Todo/5
[HttpGet("{id}", Name = "Get")]
public string Get(int id)
{
return "value";
}
// POST: api/Todo
[HttpPost]
public void Post([FromBody]string value)
{
}
// PUT: api/Todo/5
[HttpPut("{id}")]
public void Put(int id, [FromBody]string value)
{
}
// DELETE: api/Todo/5
[HttpDelete("{id}")]
public void Delete(int id)
{
}
}
}
Add dependency injection
Go to the Startup page ConfigureServices method
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.AddMvc();
}
}
Add the SmartIT.Employee.MockDB using a statement top of the startup page.
using SmartIT.Employee.MockDB;
Add the AddSigleton service. Why did we choose Sigleton? Because our service is an In-memory repository data service. We want only a single instance will be created.
using SmartIT.Employee.MockDB;
namespace TodoAngular_Ui
{
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.AddMvc();
services.AddSingleton<ITodoRepository, TodoRepository>(); // Add the services here
}
}
}
Add the top of the TodoController page
using SmartIT.Employee.MockDB;
Add private readonly ITodoRepository _todoRepository; readonly ItodoRepositor Interface member.
Add a Todo Constructor
using SmartIT.Employee.MockDB;
namespace TodoAngular.Ui.Controllers
{
[Produces("application/json")]
[Route("api/Todo")]
public class TodoController : Controller
{
private readonly ITodoRepository _todoRepository;
public TodoController(ITodoRepository todoRepository)
{
_todoRepository = todoRepository;
}
}
}
Add GetAllTodos Httpget mehot with [Route("~/api/GetAllTodos")]
Here are the final changes below on the TodoController
using SmartIT.Employee.MockDB;
namespace TodoAngular.Ui.Controllers
{
[Produces("application/json")]
[Route("api/Todo")]
public class TodoController : Controller
{
private readonly ITodoRepository _todoRepository;
public TodoController(ITodoRepository todoRepository)
{
_todoRepository = todoRepository;
}
// GET: api/Todo
[Route("~/api/GetAllTodos")]
[HttpGet]
public IEnumerable<Todo> GetAllTodos()
{
return _todoRepository.GetAll();
}
}
}
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.
Update the remainder of the TodoController like below.
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using SmartIT.Employee.MockDB;
namespace TodoAngular.Ui.Controllers
{
[Produces("application/json")]
[Route("api/Todo")]
public class TodoController : Controller
{
private readonly ITodoRepository _todoRepository;
public TodoController(ITodoRepository todoRepository)
{
_todoRepository = todoRepository;
}
[Route("~/api/GetAllTodos")]
[HttpGet]
public IEnumerable<Todo> GetAllTodos()
{
return _todoRepository.GetAll();
}
[Route("~/api/AddTodo")]
[HttpPost]
public Todo AddTodo([FromBody]Todo item)
{
return _todoRepository.Add(item);
}
[Route("~/api/UpdateTodo")]
[HttpPut]
public Todo UpdateTodo([FromBody]Todo item)
{
return _todoRepository.Update(item);
}
[Route("~/api/DeleteTodo/{id}")]
[HttpDelete]
public void Delete(int id)
{
var findTodo = _todoRepository.FindById(id);
if (findTodo != null)
_todoRepository.Delete(findTodo);
}
}
}
Create todoservice.ts
Add a Todo folder under the components directory.
Add a TypeScript file todoService.ts into the Todo directory.
yo
Paste the below code into your todoService.ts
import { Injectable } from "@angular/core";
import { Http, Response, Headers } from "@angular/http";
import "rxjs/add/operator/map";
import 'rxjs/add/operator/do'; // debug
import { Observable } from "rxjs/Observable";
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
@Injectable()
export class TodoService {
public todoList: Observable<Todo[]>;
private _todoList: BehaviorSubject<Todo[]>;
private baseUrl: string;
private dataStore: {
todoList: Todo[];
};
constructor(private http: Http) {
this.baseUrl = '/api/';
this.dataStore = { todoList: [] };
this._todoList = <BehaviorSubject<Todo[]>>new BehaviorSubject([]);
this.todoList = this._todoList.asObservable();
}
getAll() {
this.http.get(`${this.baseUrl}GetAllTodos`)
.map(response => response.json())
.subscribe(data => {
this.dataStore.todoList = data;
this._todoList.next(Object.assign({}, this.dataStore).todoList);
}, error => console.log('Could not load todo.'));
}
public addTodo(newTodo: Todo) {
console.log("addTodo");
var headers = new Headers();
headers.append('Content-Type', 'application/json; charset=utf-8');
console.log('add todo : ' + JSON.stringify(newTodo));
this.http.post(`${this.baseUrl}AddTodo/`, JSON.stringify(newTodo), { headers: headers })
.map(response => response.json())
.subscribe(data => {
this.dataStore.todoList.push(data);
this._todoList.next(Object.assign({}, this.dataStore).todoList);
}, error => console.log('Could not create todo.'));
};
public updateTodo(newTodo: Todo) {
console.log("updateTodo");
var headers = new Headers();
headers.append('Content-Type', 'application/json; charset=utf-8');
console.log('add todo : ' + JSON.stringify(newTodo));
this.http.put(`${this.baseUrl}UpdateTodo/`, JSON.stringify(newTodo), { headers: headers })
.map(response => response.json())
.subscribe(data => {
this.dataStore.todoList.forEach((t, i) => {
if (t.id === data.id) { this.dataStore.todoList[i] = data; }
});
}, error => console.log('Could not update todo.'));
};
removeItem(todoId: number) {
var headers = new Headers();
headers.append('Content-Type', 'application/json; charset=utf-8');
console.log("removeItem:" + todoId);
this.http.delete(`${this.baseUrl}DeleteTodo/${todoId}`, { headers: headers }).subscribe(response => {
this.dataStore.todoList.forEach((t, i) => {
if (t.id === todoId) { this.dataStore.todoList.splice(i, 1); }
});
this._todoList.next(Object.assign({}, this.dataStore).todoList);
}, error => console.log('Could not delete todo.'));
}
private _serverError(err: any) {
console.log('sever errorOK:', err); // debug
if (err instanceof Response) {
return Observable.throw(err.json().error || 'backend server error');
// if you're using lite-server, use the following line
// instead of the line above:
//return Observable.throw(err.text() || 'backend server error');
}
return Observable.throw(err || 'backend server error');
}
}
export class Todo {
public id: number;
public name: string;
}
Create todo.component.html
Add an HTML page into Todo directory and name it todo.component.html
Paste the below code in your todo.component.html
<div id="summary" class="section panel panel-primary">
<div class="panel-heading"> Todo Summary</div>
<div class="container">
<!--{{todoList | json}}-->
<table class="table table-striped table-condensed" >
<thead>
<tr>
<th>Id</th>
<th>Name</th>
<th></th>
</tr>
<tr>
<th> </th>
<th>
<input id="newName" [(ngModel)]='newTodo.name' type="text" placeholder="newName" />
</th>
<th>
<button class="btn btn-primary" (click)="addTodo(newTodo)">Add </button>
</th>
</tr>
</thead>
<tbody *ngFor="let item of todoList | async">
<tr>
<td>{{item.id}} </td>
<td><input type="text" [(ngModel)]='item.name' /></td>
<td>
<button class="btn btn-xs btn-primary" (click)="updateTodo(item)">Update</button>
</td>
<td>
<button class="btn btn-xs btn-primary" (click)="deleteTodo(item.id)">Remove</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
Create todo.component.ts
Add a TypeScript file called to.component.ts into the Todo directory.
Paste below code into your to.component.ts
import { Component, OnInit } from '@angular/core';
import 'rxjs/add/operator/catch';
import { TodoService } from "./todoService";
import { Observable } from "rxjs/Observable";
import { Todo } from "./todoService";
@Component({
selector: 'todo',
providers: [TodoService],
templateUrl: './todo.component.html'
})
export class TodoComponent implements OnInit {
public todoList: Observable<Todo[]>;
showEditor = true;
myName: string;
newTodo: Todo;
constructor(private dataService: TodoService) {
this.newTodo = new Todo();
}
// if you want to debug info just uncomment the console.log lines.
ngOnInit() {
// console.log("in ngOnInit");
this.todoList = this.dataService.todoList;
this.dataService.getAll();
}
public addTodo(item: Todo) {
//console.dir(item);
let todoId = this.dataService.addTodo(this.newTodo);
}
public updateTodo(item: Todo) {
// console.dir(item);
//console.log("In updateTodo: " + item);
this.dataService.updateTodo(item);
// console.log("in updateTodo:" );
}
public deleteTodo(todoId: number) {
this.dataService.removeItem(todoId);
}
}
Add new created component into ApModule
import { CounterComponent } from './components/counter/counter.component';
import { TodoComponent } from './components/todo/todo.component';
@NgModule({
declarations: [
AppComponent,
NavMenuComponent,
CounterComponent,
FetchDataComponent,
HomeComponent,
TodoComponent
],
imports: [
CommonModule,
HttpModule,
FormsModule,
RouterModule.forRoot([
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'todo', component: TodoComponent },
{ path: 'home', component: HomeComponent },
{ path: 'counter', component: CounterComponent },
{ path: 'fetch-data', component: FetchDataComponent },
{ path: '**', redirectTo: 'home' }
])
]
})
export class AppModuleShared {
Let's 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
<li [routerLinkActive]="['link-active']">
<a [routerLink]="['/todo']">
<span class='glyphicon glyphicon-user'></span> Todo
</a>
</li>
Here is the updated menu
<div class='main-nav'>
<div class='navbar navbar-inverse'>
<div class='navbar-header'>
<button type='button' class='navbar-toggle' data-toggle='collapse' data-target='.navbar-collapse'>
<span class='sr-only'>Toggle navigation</span>
<span class='icon-bar'></span>
<span class='icon-bar'></span>
<span class='icon-bar'></span>
</button>
<a class='navbar-brand' [routerLink]="['/home']">SmartIT by John Kocer</a>
</div>
<div class='clearfix'></div>
<div class='navbar-collapse collapse'>
<ul class='nav navbar-nav'>
<li [routerLinkActive]="['link-active']">
<a [routerLink]="['/todo']">
<span class='glyphicon glyphicon-user'></span> Todo
</a>
</li>
<li [routerLinkActive]="['link-active']">
<a [routerLink]="['/home']">
<span class='glyphicon glyphicon-home'></span> Home
</a>
</li>
<li [routerLinkActive]="['link-active']">
<a [routerLink]="['/counter']">
<span class='glyphicon glyphicon-education'></span> Counter
</a>
</li>
<li [routerLinkActive]="['link-active']">
<a [routerLink]="['/fetch-data']">
<span class='glyphicon glyphicon-th-list'></span> Fetch data
</a>
</li>
</ul>
</div>
</div>
</div>
Update the route module
RouterModule.forRoot([
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'todo', component: TodoComponent },
{ path: 'home', component: HomeComponent },
{ path: 'counter', component: CounterComponent },
{ path: 'fetch-data', component: FetchDataComponent },
{ path: '**', redirectTo: 'home' }
])
Here is the updated RouteModule
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { RouterModule } from '@angular/router';
import { AppComponent } from './components/app/app.component';
import { NavMenuComponent } from './components/navmenu/navmenu.component';
import { HomeComponent } from './components/home/home.component';
import { FetchDataComponent } from './components/fetchdata/fetchdata.component';
import { CounterComponent } from './components/counter/counter.component';
import { TodoComponent } from './components/todo/todo.component';
@NgModule({
declarations: [
AppComponent,
NavMenuComponent,
CounterComponent,
FetchDataComponent,
HomeComponent,
TodoComponent
],
imports: [
CommonModule,
HttpModule,
FormsModule,
RouterModule.forRoot([
{ path: '', redirectTo: 'todo', pathMatch: 'full' },
{ path: 'todo', component: TodoComponent },
{ path: 'home', component: HomeComponent },
{ path: 'counter', component: CounterComponent },
{ path: 'fetch-data', component: FetchDataComponent },
{ path: '**', redirectTo: 'todo' }
])
]
})
export class AppModuleShared { }
Here is the final result of the responsive web design result.
Summary
In this article, we will learn
- 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 is included in this training material.
You can follow the above steps to create your own Employee CRUD ASP.NET MVC Angular 4.0 application.
// Use below Employee repository to create your CRUD operation
EmployeeRepository _employeeRepository = new EmployeeRepository();
_employeeRepository.Add(new Employee() { Name = "Mat Stone", Gender = "Male", DepartmentId = 2, Salary = 8000 });
_employeeRepository.CDump("Employees");
// DebugHelper.cs(29): Employees
// { "Items":[{"Id":1,"Name":"Mike","Gender":"Male","Salary":8000,"DepartmentId":1,"Department":null},
// {"Id":2,"Name":"Adam","Gender":"Male","Salary":5000,"DepartmentId":1,"Department":null},
// {"Id":3,"Name":"Jacky","Gender":"Female","Salary":9000,"DepartmentId":1,"Department":null},
// {"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.
Resources