Mean Stack Crud App With Angular4

Bipin Swarnkar
25 min readOct 13, 2017

Part 2 (Frontend Using Angular4)

Read Part 1.

Step 2. Create the Front-End Using Angular4

This is the second part of the Mean Todo tutorial, If you haven’t read the first part please read it first. In this app, we will use the angular QuickStart as a base and generate the basic application structure using Angular CLI tool. So let’s set up the Angular CLI first.

npm install -g @angular/cli

Our next step is to generate the basic Angular4 application using Angular CLI. Run the following in your terminal:

ng new  angular-client

Please be patient, It takes time to set up a new project.

Go to your project directory and launch the server.

cd angular-client

Update the angular CLI to latest to avoid version related issues:

npm install --save-dev @angular/cli@latest

Run the basic boilerplate angular application over at http://localhost:4200.

ng serve --open

Using the --open (or just -o) option will automatically open your browser on http://localhost:4200/.

Your app greets you with a message:

The src/app/ directory inside your project angular-project will be our work space where we will create our all components, models, routes and services.

app/app.component.ts

Defines the same AppComponent as the one in the QuickStart playground.
The app component is the root component of the application, it defines the root tag of the app as <app-root></app-root> with the selector property..

app/app.module.ts

Defines AppModule, the root module that tells Angular how to assemble the application.It defines the root module of the application along with metadata about the module. Right now it declares only the AppComponent. Soon there will be more components to declare.

Bootstrap Installation

In this section, we’ll install Bootstrap and confirm that it works by making a small change that is visible in the UI. Let’s first install Bootstrap:

npm install bootstrap@3 jquery --save

We are also installing Jquery along with the bootstrap becacause Jquery is required to run the bootstrap javascript plugin. This installs Bootstrap and jQuery into the node_modules folder within the project directory.

  • angular-client/node_modules/bootstrap/dist/css/bootstrap.min.css
  • angular-client/node_modules/bootstrap/dist/js/bootstrap.min.js
  • angular-client/node_modules/jquery/dist/jquery.min.js

Now we have to link these files to our client. Open .angular-cli.json available in your project directory and add file paths to the styles and scripts array.

mean_angular2_todo/angular-client/.angular-cli.json

.....
"styles": [
"styles.css",
"../node_modules/bootstrap/dist/css/bootstrap.min.css"
],
"scripts": [
"../node_modules/jquery/dist/jquery.min.js",
"../node_modules/bootstrap/dist/js/bootstrap.min.js"
],
......

Add body padding to avoid overlapping of our fixed header in global styles file styles.js inside src directory.

mean_angular2_todo/angular-client/src/styles.css

/* You can add global styles to this file, and also import other style files */body {
padding-top: 50px;
}

Add the navbar and a bootstrap container in app.component.html.

mean_angular2_todo/angular-client/src/app/app.component.html

<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<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" href="#">Project name</a>
</div>
<div id="navbar" class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li class="active"><a href="#">Home</a></li>
<li><a href="#about">About</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
</div><!--/.nav-collapse -->
</div>
</nav>
<div class="container"><div class="starter-template">
<h1>Bootstrap starter template</h1>
<p class="lead">Use this document as a way to quickly start any new project.<br> All you get is this text and a mostly barebones HTML document.</p>
</div>

Now if your app is running you can see the bootstrap starter template in you browser.

Routing

The app routing file defines the routes of the application, each route contains a path and associated component.

First of all make sure that, the index.html inside the src folder, had the <base> element as the first child in the <head> tag:

<base href="/">

It’s a good idea to refactor the routing configuration into its own class. We will also create an app-routing.module.ts file as a sibling to app.module.ts and add the following code in app-routing.module.ts :

./angular-client/src/app/app-routing.module.ts

// ./angular-client/src/app/app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HomePageComponent } from './home.component';const routes: Routes = [
{ path: '', redirectTo: '/home', pathMatch: 'full' },
{ path: 'home', component: HomePageComponent }
];
@NgModule({
imports: [
RouterModule.forRoot(
routes,
{ enableTracing: true }
)
],
exports: [
RouterModule
]
})
export class AppRoutingModule {}

This route definition has the following parts:

  • Path: The router matches this route’s path to the URL in the browser address bar (home).
  • Component: The component that the router should create when navigating to this route (HomePageComponent).

The routes array of routes describes how to navigate. Pass it to the RouterModule.forRootmethod in the module imports to configure the router. enableTracing: true outputs each router event that took place during each navigation lifecycle to the browser console.

Now we need to create HomePageComponent imported in app-routing.module.ts.

./angular-client/src/app/home.component.ts

// ./angular-client/src/app/home.component.ts
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'my-home',
templateUrl: './home.component.html',
styleUrls:[ './home.component.css' ]
})
export class HomePageComponent implements OnInit {
ngOnInit(): void {}}

./angular-client/src/app/home.component.html

<div class="container"><div class="starter-template">
<h1>Home Page</h1>
<p class="lead">This is your home page.<br>Contents of this page are coming from component <b>./angular-client/src/app/app.component.html</b><br> You can make your dashboard here.</p>
<hr>
<h2>Latest Todos</h2>
<div class="latestTodos">
<div class="row">
<!-- Boxes de Acoes -->
<div class="col-xs-12 col-sm-6 col-lg-4">
<div class="box">
<div class="icon">
<div class="image"><i class="fa fa-tasks"></i></div>
<div class="info">
<h3 class="title">Made with Bootstrap</h3>
<div class="more">
<a href="#" title="Title Link">
Read More <i class="fa fa-angle-double-right"></i>
</a>
</div>
</div>
</div>
<div class="space"></div>
</div>
</div>
<div class="col-xs-12 col-sm-6 col-lg-4">
<div class="box">
<div class="icon">
<div class="image"><i class="fa fa-tasks"></i></div>
<div class="info">
<h3 class="title">Icons by Font Awesome</h3>
<div class="more">
<a href="#" title="Title Link">
Read More <i class="fa fa-angle-double-right"></i>
</a>
</div>
</div>
</div>
<div class="space"></div>
</div>
</div>
<div class="col-xs-12 col-sm-6 col-lg-4">
<div class="box">
<div class="icon">
<div class="image"><i class="fa fa-tasks"></i></div>
<div class="info">
<h3 class="title">Desktop Friendly</h3>
<div class="more">
<a href="#" title="Title Link">
Read More <i class="fa fa-angle-double-right"></i>
</a>
</div>
</div>
</div>
<div class="space"></div>
</div>
</div>

</div>
</div>
</div>

./angular-client/src/app/home.component.css

@import "//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css";
@import "http://fonts.googleapis.com/css?family=Roboto:400,500";
.starter-template {
padding: 40px 15px;
text-align: center;
}
.box > .icon { text-align: center; position: relative; }
.box > .icon > .image { position: relative; z-index: 2; margin: auto; width: 88px; height: 88px; border: 8px solid white; line-height: 88px; border-radius: 50%; background: #63B76C; vertical-align: middle; }
.box > .icon:hover > .image { background: #333; }
.box > .icon > .image > i { font-size: 36px !important; color: #fff !important; }
.box > .icon:hover > .image > i { color: white !important; }
.box > .icon > .info { margin-top: -24px; background: rgba(0, 0, 0, 0.04); border: 1px solid #e0e0e0; padding: 15px 0 10px 0; }
.box > .icon:hover > .info { background: rgba(0, 0, 0, 0.04); border-color: #e0e0e0; color: white; }
.box > .icon > .info > h3.title { font-family: "Robot",sans-serif !important; font-size: 16px; color: #222; font-weight: 700; }
.box > .icon:hover > .info > h3.title, .box > .icon:hover > .info > p, .box > .icon:hover > .info > .more > a { color: #222; }
.box > .icon > .info > .more a { font-family: "Robot",sans-serif !important; font-size: 12px; color: #222; line-height: 12px; text-transform: uppercase; text-decoration: none;cursor: pointer;}
.box > .icon:hover > .info > .more > a { color: #fff; padding: 6px 8px; background-color: #63B76C; }
.box .space { height: 30px; }

Router outlet.

We just finished our app routing configuration, now you have to tell the router where to display the component. To do this, you can add a <router-outlet> element at the end of the template. RouterOutlet is one of the directives provided by the RouterModule. The router displays each component immediately below the <router-outlet> as users navigate through the app. We will add RouterOutlet in our app.component.html.

./angular-client/src/app/app.component.html

<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<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" href="#">Project name</a>
</div>
<div id="navbar" class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li routerLinkActive="active"><a routerLink="/">Home</a></li>
<li routerLinkActive="active"><a routerLink="/todos">About</a></li>
</ul>
</div><!--/.nav-collapse -->
</div>
</nav>
<router-outlet></router-outlet>

The RouterLink directive (another of the RouterModule directives) is bound to a string that tells the router where to navigate when the user clicks the link. The RouterLinkActive directive on each anchor tag helps visually distinguish the anchor for the currently selected "active" route. The router adds the active CSS class to the element when the associated RouterLink becomes active.

Update AppModule.

You need to configure your app.module.ts to work your router. Revised AppModule :

./angular-client/src/app/app.module.ts

// ./angular-client/src/app/app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { HomePageComponent } from './home.component';
import { AppRoutingModule } from './app-routing.module';@NgModule({
declarations: [
AppComponent,
HomePageComponent
],
imports: [
BrowserModule,
AppRoutingModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

At this point, the project has a basic configuration with app routing. Let’s run our app.

ng serve

If your app is already running don’t restart it, because it will automatically reflect the updates. You will get the look given below in your browser:

Now our basic app with routing and home page template is ready. Let’s consume our express server Api to show the dynamic latest todos in our home page.

Let’s import HttpModule from the @angular/http library and add HttpModule to the importslist of the AppModule which will allow us to access to these services from anywhere in the app. Also, import a Todo service we are going to create.

./angular-client/src/app/app.module.ts

// ./angular-client/src/app/app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpModule } from '@angular/http';
import { AppComponent } from './app.component';
import { HomePageComponent } from './home.component';
import { AppRoutingModule } from './app-routing.module';
import { TodoService } from './todo/todo.service';
@NgModule({
declarations: [
AppComponent,
HomePageComponent
],
imports: [
BrowserModule,
AppRoutingModule,
HttpModule
],
providers: [ TodoService ],
bootstrap: [AppComponent]
})
export class AppModule { }

We need to create the Todo service we had imported in app.module.ts . Let’s create our Todo service in a new folder todo.

./angular-client/src/app/todo/todo.service.ts

// ./angular-client/src/app/todo/todo.service.ts
import { Injectable } from '@angular/core';
import { Headers, Http } from '@angular/http';
import 'rxjs/add/operator/toPromise';@Injectable()
export class TodoService {
private apiUrl = 'http://localhost:3001/api';
constructor(private http: Http){ }
getTodos(): Promise<any>{
return this.http.get(this.apiUrl)
.toPromise()
.then(this.handleData)
.catch(this.handleError)
}
private handleData(res: any) {
let body = res.json();
console.log(body); // for development purposes only
return body || {};
}
private handleError(error: any): Promise<any> {
console.error('An error occurred', error); // for development purposes only
return Promise.reject(error.message || error);
}
}

In the code above in todo.service.js , Angular http.get returns an RxJS Observable. Observables are a powerful way to manage asynchronous data flows. You'll read about Observables later in this page. For now, you’ve converted the Observable to a Promise using the toPromise() operator.

The Angular Observable doesn't have a toPromise operator of its own. But, there are many operators like toPromise that extend Observable with useful capabilities. To use those capabilities, you have to add the operators themselves. That's as easy as importing them from the RxJS library like this:

import 'rxjs/add/operator/toPromise';

In the Promise’s then() callback, we are invoking the handleData(res:any)to call the json method of the HTTP Response to extract the data within the response and returning the data. Also, we are catching the error and passing them to our error handler handleError(error: any) . In the error handler, we are forwarding an error message as a failed promise instead of a failed promises.

Now, we need to revise home.component.ts and home.component.html to consume our Todo Service.

./angular-client/src/app/home.component.ts

// ./angular-client/src/app/home.component.ts
import { Component, OnInit } from '@angular/core';
import { TodoService } from './todo/todo.service';@Component({
selector: 'my-home',
templateUrl: './home.component.html',
styleUrls:[ './home.component.css' ]
})
export class HomePageComponent implements OnInit {
todos:any[] = [];
constructor(private todoService: TodoService) { }
ngOnInit(): void {
this.todoService.getTodos()
.then(todos => this.todos = todos.todos.reverse().slice(0,3))
}
}

In the code above we are using the Angular ngOnInit lifecycle hook to call the getHeroes() at the right time. In order to get the last three elements of the todos array, we are reversing the todos array using reverse() and slicing the array using array.slice(start, end) . The slice() method selects the elements starting at the given start argument, and ends at, but does not include, the given end argument. We need to revise our home.component.html to reflect the server data.

./angular-client/src/app/home.component.html

<div class="container"><div class="starter-template">
<h1>Home Page</h1>
<p class="lead">This is your home page.<br>Contents of this page are coming from component <b>./angular-client/src/app/app.component.html</b><br> You can make your dashboard here.</p>
<hr>
<h2>Latest Todos</h2>
<div class="latestTodos">
<div class="row">
<!-- Boxes -->
<div *ngFor="let todo of todos" class="col-xs-12 col-sm-6 col-lg-4">
<div class="box">
<div class="icon">
<div class="image"><i class="fa fa-tasks"></i></div>
<div class="info">
<h3 class="title">{{todo.todoText}}</h3>
<div class="more">
<a [routerLink]="['/todo', todo._id]" title="
Click to Open {{todo.todoText}}">
Read More <i class="fa fa-angle-double-right"></i>
</a>
</div>
</div>
</div>
<div class="space"></div>
</div>
</div>
</div>
</div>
</div>

At this point, the project doesn’t have the Add Todo functionality so we need to use Postman API Development Environment to test our latest todos in home page. Let’s add some todos to our database using our express server Api in Postman.

After adding some todos using Postman, we can see the last three todos in our latest todos on refreshing the home page.

List Todos

We are going to create a new route Todos, where we will all user to perform crud operations like GET, POST, PUT, DELETE on todos.

Till now we have used Angular CLI for building our basic structure of the project. But from now we will use it to add features in our existing Angular application. Let’s add our Todo List component inside our todo directory.

ng g component todo/todo-list

You can see a new directory todo-list with some new files in it created. Now we will create our Todo route.

ng g module todo/todo-routing

Let’s configure our Todo routing

./angular-client/src/app/todo/todo-routing/todo-routing.module.ts

// ./angular-client/src/app/todo/todo-routing/todo-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { TodoListComponent } from '../todo-list/todo-list.component';const todoRoutes: Routes = [
{ path:'todos', component:TodoListComponent }
]
@NgModule({
imports: [
RouterModule.forChild(todoRoutes)
],
exports: [
RouterModule
],

declarations: []
})
export class TodoRoutingModule { }

Next, update the app.module.ts file, first importing the newly created TodoRoutingModulefrom todo-routing.module.ts, then import TodoListComponent from todo-list.component.ts . Update module imports array and declarations. Revised app.module.ts :

./angular-client/src/app/app.module.ts

// ./angular-client/src/app/app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpModule } from '@angular/http';
import { AppComponent } from './app.component';
import { HomePageComponent } from './home.component';
import { AppRoutingModule } from './app-routing.module';
import { TodoRoutingModule } from './todo/todo-routing/todo-routing.module';
import { TodoService } from './todo/todo.service';
import { TodoListComponent } from './todo/todo-list/todo-list.component';
@NgModule({
declarations: [
AppComponent,
HomePageComponent,
TodoListComponent
],
imports: [
BrowserModule,
AppRoutingModule,
HttpModule,
TodoRoutingModule
],
providers: [ TodoService ],
bootstrap: [AppComponent]
})
export class AppModule { }

Also you need to update the routerLink in app.component.html .

./angular-client/src/app/app.component.html

<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<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" href="#">Project name</a>
</div>
<div id="navbar" class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li routerLinkActive="active"><a routerLink="/">Home</a></li>
<li routerLinkActive="active"><a routerLink="/todos">Todos</a></li>
</ul>
</div><!--/.nav-collapse -->
</div>
</nav>
<div class="container">
<router-outlet></router-outlet>
</div>

You can now navigate your favourite browser to http://localhost:4200/ to see your application is running fine with your Todo Route :

Next Update todo-list.component.ts and todo-list.component.html to consume the real todos we are fetching in our Todo Service.

./angular-client/src/app/todo/todo-list/todo-list.component.ts

// ./angular-client/src/app/todo/todo-list/todo-list.component.ts
import { Component, OnInit } from '@angular/core';
import { TodoService } from '../todo.service';@Component({
selector: 'app-todo-list',
templateUrl: './todo-list.component.html',
styleUrls: ['./todo-list.component.css']
})
export class TodoListComponent implements OnInit {
todos:any[] = [];
constructor(private todoService:TodoService) { }
ngOnInit(): void {
this.todoService.getTodos()
.then(td => this.todos = td.todos )
}
}

./angular-client/src/app/todo/todo-list/todo-list.component.html

<h2 align="center">Your Todo's List</h2>
<table id="mytable" class="table table-bordred table-striped">
<thead>
<th>Todo</th>
<th>View</th>
<th>Edit</th>
<th>Delete</th>
</thead>
<tbody>
<tr *ngFor="let todo of todos">
<td>{{todo.todoText}}</td>
<td><a [routerLink]="['/todo', todo._id]" title="Click to Open {{todo.todoText}}">View</a></td>
<td><p data-placement="top" data-toggle="tooltip" title="Edit"><button class="btn btn-primary btn-xs" data-title="Edit" data-toggle="modal" data-target="#edit" ><span class="glyphicon glyphicon-pencil"></span></button></p></td>
<td><p data-placement="top" data-toggle="tooltip" title="Delete"><button class="btn btn-danger btn-xs" data-title="Delete" data-toggle="modal" data-target="#delete" ><span class="glyphicon glyphicon-trash"></span></button></p></td>
</tr>
<tr>
</tbody>
</table>

Let’s have a look at listing Todo’s getting from our express server:

At this point todo events edit, delete, and view are not working at all. But we will make them work one by one. So let’s grab the view Todo first.

Add getTodo(id:number) method in Todo service

./angular-client/src/app/todo/todo.service.ts

// ./angular-client/src/app/todo/todo.service.ts
import { Injectable } from '@angular/core';
import { Headers, Http } from '@angular/http';
import 'rxjs/add/operator/toPromise';@Injectable()
export class TodoService {
private apiUrl = 'http://localhost:3001/api/';
constructor(private http: Http){ }
getTodos(): Promise<any>{
return this.http.get(this.apiUrl)
.toPromise()
.then(this.handleData)
.catch(this.handleError)
}
getTodo(id:string): Promise<any>{
return this.http.get(this.apiUrl + id)
.toPromise()
.then(this.handleData)
.catch(this.handleError)
}
private handleData(res: any) {
let body = res.json();
console.log(body); // for development purposes only
return body || {};
}
private handleError(error: any): Promise<any> {
console.error('An error occurred', error); // for development purposes only
return Promise.reject(error.message || error);
}
}

Add new component Todo detail.

ng g component todo/todo-detail

Add TodoDetailComponent with new path todo/:id in todo-routing.module.ts

// ./angular-client/src/app/todo/todo-routing/todo-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { TodoListComponent } from '../todo-list/todo-list.component';
import { TodoDetailComponent } from '../todo-detail/todo-detail.component'
const todoRoutes: Routes = [
{ path:'todos', component:TodoListComponent },
{ path:'todo/:id', component:TodoDetailComponent }
]
@NgModule({
imports: [
RouterModule.forChild(todoRoutes)
],
exports: [
RouterModule
],
declarations: []
})
export class TodoRoutingModule { }

Modify your todo-detail.components.ts and todo-detail.component.html

./angular-client/src/app/todo/todo-detail/todo-detail.component.ts

// ./angular-client/src/app/todo/todo-detail/todo-detail.component.ts
import { Component, OnInit } from '@angular/core';
import 'rxjs/add/operator/switchMap';
import { ActivatedRoute, ParamMap } from '
@angular/router';
import { Location } from '
@angular/common';
import { TodoService } from '../todo.service';@Component({
selector: 'app-todo-detail',
templateUrl: './todo-detail.component.html',
styleUrls: ['./todo-detail.component.css']
})
export class TodoDetailComponent implements OnInit {
todo:any[]=[];
constructor(
private todoService:TodoService,
private route:ActivatedRoute,
private location:Location

) { }
ngOnInit():void {
this.route.paramMap
.switchMap((params:ParamMap) => this.todoService.getTodo(params.get('id')))
.subscribe(td => this.todo = td.todo[0])

}
goBack():void {
this.location.back();
}
}

./angular-client/src/app/todo/todo-detail/todo-detail.component.html

<div align="center">
<h4>Todo Detail</h4>
<div *ngIf="todo">
<h3>{{todo.todoText}}</h3>
<p>Description: {{todo.todoDesc}}</p>
<button (click)="goBack()" type="button" class="btn btn-danger">
<span class="glyphicon glyphicon-chevron-left"></span>
Go Back
</button>
</div>
</div>

Import TodoDetailComponent in app.module.ts

./angular-client/src/app/app.module.ts

// ./angular-client/src/app/app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpModule } from '@angular/http';
import { AppComponent } from './app.component';
import { HomePageComponent } from './home.component';
import { AppRoutingModule } from './app-routing.module';
import { TodoRoutingModule } from './todo/todo-routing/todo-routing.module';
import { TodoService } from './todo/todo.service';
import { TodoListComponent } from './todo/todo-list/todo-list.component';
import { TodoDetailComponent } from './todo/todo-detail/todo-detail.component';
@NgModule({
declarations: [
AppComponent,
HomePageComponent,
TodoListComponent,
TodoDetailComponent
],
imports: [
BrowserModule,
AppRoutingModule,
HttpModule,
TodoRoutingModule
],
providers: [ TodoService ],
bootstrap: [AppComponent]
})
export class AppModule { }

You will get the following when click on view in Todos List:

We just finished our Todo routing. Let’s move to Add Todo.

Add Todo

First we need a AddTodo Button or hyperlink which will show/hide the AddTodo Form when clicked by the user. Let’s add new hyperlink in app.component.html.

./angular-client/src/app/app.component.html

<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<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" href="#">Project name</a>
</div>
<div id="navbar" class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li routerLinkActive="active"><a routerLink="/">Home</a></li>
<li routerLinkActive="active"><a routerLink="/todos">Todos</a></li>
</ul>
<ul *ngIf="routeUrl == todos" class="nav navbar-nav navbar-right">
<li><a (click)="showAddTodoBox($event)" href="#" >Add Todo</a></li>
</ul>
</div><!--/.nav-collapse -->
</div>
</nav>
<div class="container">
<router-outlet></router-outlet>
</div>

Add the Todo form inside a new div in todo-list.component.html.

./angular-client/src/app/todo/todo-list/todo-list.component.html

<div align="center" class="AddTodoBox" [hidden]="todoService.showAddTodoBox">
<h4>Add New Todo</h4>
<form>
<div class="form-group">
<label for="todoText">Todo:</label>
<input type="text" class="form-control" name="todoText" id="todoText">
</div>
<div class="form-group">
<label for="todoDesc">Description:</label>
<textarea class="form-control" name="todoDesc" id="todoDesc"></textarea>
</div>
<button type="submit" class="btn btn-success btn-block">Submit</button>
</form>
</div>
<h2 align="center">Your Todo's List</h2>
<table id="mytable" class="table table-bordred table-striped">
<thead>
<th>Todo</th>
<th>View</th>
<th>Edit</th>
<th>Delete</th>
</thead>
<tbody>
<tr *ngFor="let todo of todos">
<td>{{todo.todoText}}</td>
<td><a [routerLink]="['/todo', todo._id]" title="Click to Open {{todo.todoText}}">View</a></td>
<td><p data-placement="top" data-toggle="tooltip" title="Edit"><button class="btn btn-primary btn-xs" data-title="Edit" data-toggle="modal" data-target="#edit" ><span class="glyphicon glyphicon-pencil"></span></button></p></td>
<td><p data-placement="top" data-toggle="tooltip" title="Delete"><button class="btn btn-danger btn-xs" data-title="Delete" data-toggle="modal" data-target="#delete" ><span class="glyphicon glyphicon-trash"></span></button></p></td>
</tr>
<tr>
</tbody>
</table>

Next Add a new Boolean Variable in our todo.service.ts.

// ./angular-client/src/app/todo/todo.service.ts
import { Injectable } from '@angular/core';
import { Headers, Http } from '@angular/http';
import 'rxjs/add/operator/toPromise';@Injectable()
export class TodoService {
private apiUrl = 'http://localhost:3001/api/';
showAddTodoBox:boolean = true;
constructor(private http: Http){ }
getTodos(): Promise<any>{
return this.http.get(this.apiUrl)
.toPromise()
.then(this.handleData)
.catch(this.handleError)
}
getTodo(id:string): Promise<any>{
return this.http.get(this.apiUrl + id)
.toPromise()
.then(this.handleData)
.catch(this.handleError)
}
private handleData(res: any) {
let body = res.json();
console.log(body); // for development purposes only
return body || {};
}
private handleError(error: any): Promise<any> {
console.error('An error occurred', error); // for development purposes only
return Promise.reject(error.message || error);
}
}

Since a service is a singleton, all of the components that inject this service can share the data from this service. We will use the showAddTodoBox Boolean service variable for toggling our AddTodo form div.

./angular-client/src/app/app.component.ts

// ./angular-client/src/app/app.component.ts
import { Component } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { TodoService } from './todo/todo.service';@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app';
constructor(private todoService:TodoService) { }
// class method for toggling AddTodoBox in todo-list.component.html
showAddTodoBox(e):void{
e.preventDefault();
this.todoService.showAddTodoBox = !this.todoService.showAddTodoBox;
}
}}

Now initialize the showAddTodoBox inside the ngOnInit() lifecycle hook method that Angular calls shortly after creating the todo-list component. Because we want our AddTodo Box hidden = true; on component load.

./angular-client/src/app/todo/todo-list/todo-list.component.ts

// ./angular-client/src/app/todo/todo-list/todo-list.component.ts
import { Component, OnInit } from '@angular/core';
import { TodoService } from '../todo.service';@Component({
selector: 'app-todo-list',
templateUrl: './todo-list.component.html',
styleUrls: ['./todo-list.component.css']
})
export class TodoListComponent implements OnInit {
todos:any[] = [];
constructor(private todoService:TodoService) { }ngOnInit(): void {
this.todoService.showAddTodoBox = true;
this.todoService.getTodos()
.then(td => this.todos = td.todos )
}
}

Now you can toggle your Add Todo form on clicking Add Todo hyperlink on navbar.

Great, we just created our Todo form with show/hide functionality. As of now the form will not add the Todo, But we will make our form working on hitting the submit button in the given form.

Add the ability to Add Todo

Revise your todo-list.component.html as following:

./angular-client/src/app/todo/todo-list/todo-list.component.html

<div align="center" class="AddTodoBox" [hidden]="todoService.showAddTodoBox">
<h4>Add New Todo</h4>
<form (ngSubmit)="AddTodo(todo);todoForm.reset()" #todoForm="ngForm">
<div class="form-group">
<label for="todoText">Todo:</label>
<input type="text" [(ngModel)]="todo.todoText" #todotext="ngModel" class="form-control" name="todoText" id="todoText" required />
<div [hidden]="todotext.valid || todotext.pristine"
class="alert alert-danger">
Todo is required
</div>

</div>
<div class="form-group">
<label for="todoDesc">Description:</label>
<textarea class="form-control" [(ngModel)]="todo.todoDesc" #tododesc="ngModel" name="todoDesc" id="todoDesc" required></textarea>
<div [hidden]="tododesc.valid || tododesc.pristine"
class="alert alert-danger">
Description is required
</div>

</div>
<button type="submit" [disabled]="!todoForm.form.valid" class="btn btn-success btn-block">Submit</button>
</form>
</div>
<div *ngIf="todos.length > 0" class="TodoListBox">
<h2 align="center">Your Todo's List</h2>
<table id="mytable" class="table table-bordred table-striped">
<thead>
<th>Todo</th>
<th>View</th>
<th>Edit</th>
<th>Delete</th>
</thead>
<tbody>
<tr *ngFor="let todo of todos">
<td>{{todo.todoText}}</td>
<td><a [routerLink]="['/todo', todo._id]" title="Click to Open {{todo.todoText}}">View</a></td>
<td><p data-placement="top" data-toggle="tooltip" title="Edit"><button class="btn btn-primary btn-xs" data-title="Edit" data-toggle="modal" data-target="#edit" ><span class="glyphicon glyphicon-pencil"></span></button></p></td>
<td><p data-placement="top" data-toggle="tooltip" title="Delete"><button class="btn btn-danger btn-xs" data-title="Delete" data-toggle="modal" data-target="#delete" ><span class="glyphicon glyphicon-trash"></span></button></p></td>
</tr>
<tr>
</tbody>
</table>
</div>
<div *ngIf="todos.length <= 0" class="NoTodosBox">
<div align="center" class="alert alert-info" role="alert">
<strong>No Todos Available in Database</strong>
</div>
</div>

In the above code, [hidden]=”todoService.showAddTodoBox” is used for toggling the Todo Form Div. In angular 4 there are two ways to show/hide element *ngIf and [hidden] . *ngIf effectively removes its content from the DOM while [hidden] modifies the displayproperty and only instructs the browser to not show the content but the DOM still contains it.

Then we have used [(ngModel)] syntax for two-way data binding. We have defined a template reference variable, #todoForm, and initialized it with the value "ngForm" , we can use that variable to access the form with the Submit button. Next we have used template reference variable #todotext=”ngModel” and #tododesc=”ngModel” . The template reference variable named todotext, declared on the <input> element, refers to the <input> element itself. Also, we did the form validation using ngModel and template reference variable.

The NgModel directive doesn’t just track state; It also tells you if the user touched the control, if the value changed, or if the value became invalid.

Next we need to revise our todo-list.component.html to consume the Todo form we just written in the template. Let’s do it.

// ./angular-client/src/app/todo/todo-list/todo-list.component.ts
import { Component, OnInit } from '@angular/core';
import { TodoService } from '../todo.service';@Component({
selector: 'app-todo-list',
templateUrl: './todo-list.component.html',
styleUrls: ['./todo-list.component.css']
})
export class TodoListComponent implements OnInit {
todos:any[] = [];
todo:any = {};
constructor(private todoService:TodoService) { }ngOnInit(): void {
this.todoService.showAddTodoBox = true;
this.todoService.getTodos()
.then(td => this.todos = td.todos )
}
AddTodo(todo:any):void{
if(!todo){ return; }
this.todoService.createTodo(todo)
.then(td => {
console.log(td);
this.todos.push(td.todo);
})
}
}

We just added AddTodo(todo:any) method in todo-list.component.ts, which will use the createTodo(todo:any) method we will add in our Todo Service. Let’s update our Todo Service.

// ./angular-client/src/app/todo/todo.service.ts
import { Injectable } from '@angular/core';
import { Headers, Http } from '@angular/http';
import 'rxjs/add/operator/toPromise';@Injectable()
export class TodoService {
private apiUrl = 'http://localhost:3001/api/';
showAddTodoBox:boolean = true;

constructor(private http: Http){ }
getTodos(): Promise<any>{
return this.http.get(this.apiUrl)
.toPromise()
.then(this.handleData)
.catch(this.handleError)
}
getTodo(id:string): Promise<any>{
return this.http.get(this.apiUrl + id)
.toPromise()
.then(this.handleData)
.catch(this.handleError)
}
createTodo(todo:any): Promise<any>{
return this.http.post(this.apiUrl, todo)
.toPromise()
.then(this.handleData)
.catch(this.handleError)
}
private handleData(res: any) {
let body = res.json();
console.log(body); // for development purposes only
return body || {};
}
private handleError(error: any): Promise<any> {
console.error('An error occurred', error); // for development purposes only
return Promise.reject(error.message || error);
}
}

We have already discussed that Angular http.post returns an RxJS Observable. Observables are a powerful way to manage asynchronous data flows. In the above code we have converted the Observable to a Promise using the toPromise operator. The Angular Observable doesn't have a toPromise operator out of the box, So we have used RxJS library.

Next we need to revise our app.module.ts :

./angular-client/src/app/app.module.ts

// ./angular-client/src/app/app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpModule } from '@angular/http';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { HomePageComponent } from './home.component';
import { AppRoutingModule } from './app-routing.module';
import { TodoRoutingModule } from './todo/todo-routing/todo-routing.module';
import { TodoService } from './todo/todo.service';
import { TodoListComponent } from './todo/todo-list/todo-list.component';
import { TodoDetailComponent } from './todo/todo-detail/todo-detail.component';
@NgModule({
declarations: [
AppComponent,
HomePageComponent,
TodoListComponent,
TodoDetailComponent
],
imports: [
BrowserModule,
AppRoutingModule,
HttpModule,
TodoRoutingModule,
FormsModule
],
providers: [ TodoService ],
bootstrap: [AppComponent]
})
export class AppModule { }

In the above code, we have added FormsModule to the list of imports defined in the @NgModuledecorator. This gives the application access to all of the template-driven forms features, including ngModel.

Add some css to make the UI interactive.

./angular-client/src/app/todo/todo-list/todo-list.component.css

.AddTodoBox{
width: 50%;
margin: 20px auto;
border: 2px solid #EE6E73;
padding: 10px;
}
.AddTodoBox h4{
background-color: #EE6E73;
color: white;
padding: 5px;
}
.ng-valid[required], .ng-valid.required {
border-left: 5px solid #42A948; /* green */
}
.ng-invalid:not(form) {
border-left: 5px solid #a94442; /* red */
}

Now you will get the following look :

(Submit Button disabled if form inputs are empty)

As soon as you will fill the form, submit button becomes enabled, now you can hit the submit button and add your todo to MongoDb by consuming the express server API.

Submit button enabled

We just finished the Add Todo. Let’s move to Edit or Update Todo.

Edit Todo

First of all Let’s create a update method in Todo service:

// ./angular-client/src/app/todo/todo.service.ts
import { Injectable } from '@angular/core';
import { Headers, Http } from '@angular/http';
import 'rxjs/add/operator/toPromise';@Injectable()
export class TodoService {
private apiUrl = 'http://localhost:3001/api/';
showAddTodoBox:boolean = true;
constructor(private http: Http){ }getTodos(): Promise<any>{
return this.http.get(this.apiUrl)
.toPromise()
.then(this.handleData)
.catch(this.handleError)
}
getTodo(id:string): Promise<any>{
return this.http.get(this.apiUrl + id)
.toPromise()
.then(this.handleData)
.catch(this.handleError)
}
createTodo(todo:any): Promise<any>{
return this.http.post(this.apiUrl, todo)
.toPromise()
.then(this.handleData)
.catch(this.handleError)
}
updateTodo(todo:any):Promise<any>{
return this.http
.put(this.apiUrl, todo)
.toPromise()
.then(this.handleData)
.catch(this.handleData);
}
private handleData(res: any) {
let body = res.json();
console.log(body); // for development purposes only
return body || {};
}
private handleError(error: any): Promise<any> {
console.error('An error occurred', error); // for development purposes only
return Promise.reject(error.message || error);
}
}

Consume the Update Service method in todo-list.component.ts.

./angular-client/src/app/todo/todo-list/todo-list.component.ts

// ./angular-client/src/app/todo/todo-list/todo-list.component.ts
import { Component, OnInit } from '@angular/core';
import { TodoService } from '../todo.service';@Component({
selector: 'app-todo-list',
templateUrl: './todo-list.component.html',
styleUrls: ['./todo-list.component.css']
})
export class TodoListComponent implements OnInit {
todos:any[] = [];
todo:any = {};
todoToEdit:any = {};
apiMessage:string;
constructor(private todoService:TodoService) { }ngOnInit(): void {
this.todoService.showAddTodoBox = true;
this.todoService.getTodos()
.then(td => this.todos = td.todos )
}
AddTodo(todo:any):void{
if(!todo){ return; }
this.todoService.createTodo(todo)
.then(td => {
console.log(td);
this.todos.push(td.todo);
})
}
showEditTodo(todo:any):void{
this.todoToEdit = todo;
this.apiMessage = "";
}
EditTodo(todo:any):void{
if(!todo){ return; }
todo.id = this.todoToEdit._id;
this.todoService.updateTodo(todo)
.then(td => {
const updatedTodos = this.todos.map(t => {
if(td.todo._id !== t._id){
return t;
}
return { ...t, ...td.todo };
})
this.apiMessage = td.message;
this.todos = updatedTodos;
})
}
}

Add Edit Todo Form inside a Bootstrap Modal in todo-list.component.html.

./angular-client/src/app/todo/todo-list/todo-list.component.html

<div align="center" class="AddTodoBox" [hidden]="todoService.showAddTodoBox">
<h4>Add New Todo</h4>
<form (ngSubmit)="AddTodo(todo);todoForm.reset()" #todoForm="ngForm">
<div class="form-group">
<label for="todoText">Todo:</label>
<input type="text" [(ngModel)]="todo.todoText" #todotext="ngModel" class="form-control" name="todoText" id="todoText" required />
<div [hidden]="todotext.valid || todotext.pristine"
class="alert alert-danger">
Todo is required
</div>
</div>
<div class="form-group">
<label for="todoDesc">Description:</label>
<textarea class="form-control" [(ngModel)]="todo.todoDesc" #tododesc="ngModel" name="todoDesc" id="todoDesc" required></textarea>
<div [hidden]="tododesc.valid || tododesc.pristine"
class="alert alert-danger">
Description is required
</div>
</div>
<button type="submit" [disabled]="!todoForm.form.valid" class="btn btn-success btn-block">Submit</button>
</form>
</div>
<div *ngIf="todos.length > 0" class="TodoListBox">
<h2 align="center">Your Todo's List</h2>
<table id="mytable" class="table table-bordred table-striped">
<thead>
<th>Todo</th>
<th>View</th>
<th>Edit</th>
<th>Delete</th>
</thead>
<tbody>
<tr *ngFor="let todo of todos">
<td>{{todo.todoText}}</td>
<td><a [routerLink]="['/todo', todo._id]" title="Click to Open {{todo.todoText}}">View</a></td>
<td><p data-placement="top" data-toggle="tooltip" title="Edit"><button class="btn btn-primary btn-xs" (click) = "showEditTodo(todo)" data-title="Edit" data-toggle="modal" data-target="#edit" ><span class="glyphicon glyphicon-pencil"></span></button></p></td>
<td><p data-placement="top" data-toggle="tooltip" title="Delete"><button class="btn btn-danger btn-xs" data-title="Delete" data-toggle="modal" data-target="#delete" ><span class="glyphicon glyphicon-trash"></span></button></p></td>
</tr>
<tr>
</tbody>
</table>
<!-- Modal -->
<div class="modal fade" id="edit" role="dialog">
<div class="modal-dialog modal-md">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h4 class="modal-title">Edit Todo</h4>
</div>
<div class="modal-body">
<div align="center" class="EditTodoBox">
<form (ngSubmit)="EditTodo(todoToEdit)" #editTodoForm="ngForm">
<div class="form-group">
<label for="todoText">Todo:</label>
<input type="text" [(ngModel)]="todoToEdit.todoText" #todoedittext="ngModel" class="form-control" name="todoText" id="todoText" required />
<div [hidden]="todoedittext.valid || todoedittext.pristine"
class="alert alert-danger">
Todo is required
</div>
</div>
<div class="form-group">
<label for="todoDesc">Description:</label>
<textarea class="form-control" [(ngModel)]="todoToEdit.todoDesc" #todoeditdesc="ngModel" name="todoDesc" id="todoDesc" required></textarea>
<div [hidden]="todoeditdesc.valid || todoeditdesc.pristine"
class="alert alert-danger">
Description is required
</div>
</div>
<button type="submit" [disabled]="!editTodoForm.form.valid || !editTodoForm.form.dirty" class="btn btn-success btn-block">Submit</button>
</form>
<div style="margin:10px;" *ngIf="apiMessage" align="center" class="alert alert-success" role="alert">
<strong>{{apiMessage}}</strong>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>

</div>
<div *ngIf="todos.length <= 0" class="NoTodosBox">
<div align="center" class="alert alert-info" role="alert">
<strong>No Todos Available in Database</strong>
</div>
</div>

Now if you click on the edit button in todos you can get the following look:

Edit Todo Form
Edit Todo Form With Success Message

At this point, you can Create Todo, Edit Todo. Let’s move to Delete Todo.

Delete Todo

Add a Delete method in Todo Service:

./angular-client/src/app/todo/todo.service.ts

// ./angular-client/src/app/todo/todo.service.ts
import { Injectable } from '@angular/core';
import { Headers, Http } from '@angular/http';
import 'rxjs/add/operator/toPromise';@Injectable()
export class TodoService {
private apiUrl = 'http://localhost:3001/api/';
showAddTodoBox:boolean = true;
constructor(private http: Http){ }getTodos(): Promise<any>{
return this.http.get(this.apiUrl)
.toPromise()
.then(this.handleData)
.catch(this.handleError)
}
getTodo(id:string): Promise<any>{
return this.http.get(this.apiUrl + id)
.toPromise()
.then(this.handleData)
.catch(this.handleError)
}
createTodo(todo:any): Promise<any>{
return this.http.post(this.apiUrl, todo)
.toPromise()
.then(this.handleData)
.catch(this.handleError)
}
updateTodo(todo:any):Promise<any>{
return this.http
.put(this.apiUrl, todo)
.toPromise()
.then(this.handleData)
.catch(this.handleData);
}
deleteTodo(todo:any):Promise<any>{
return this.http
.delete(this.apiUrl + todo._id)
.toPromise()
.then(this.handleData)
.catch(this.handleError);
}
private handleData(res: any) {
let body = res.json();
console.log(body); // for development purposes only
return body || {};
}
private handleError(error: any): Promise<any> {
console.error('An error occurred', error); // for development purposes only
return Promise.reject(error.message || error);
}
}

Revise todo-list.component.ts to consume the Delete method in Todo service:

./angular-client/src/app/todo/todo-list/todo-list.component.ts

// ./angular-client/src/app/todo/todo-list/todo-list.component.ts
import { Component, OnInit } from '@angular/core';
import { TodoService } from '../todo.service';@Component({
selector: 'app-todo-list',
templateUrl: './todo-list.component.html',
styleUrls: ['./todo-list.component.css']
})
export class TodoListComponent implements OnInit {
todos:any[] = [];
todo:any = {};
todoToEdit:any = {};
todoToDelete:any = {};
apiMessage:string;
constructor(private todoService:TodoService) { }ngOnInit(): void {
this.todoService.showAddTodoBox = true;
this.todoService.getTodos()
.then(td => this.todos = td.todos )
}
AddTodo(todo:any):void{
if(!todo){ return; }
this.todoService.createTodo(todo)
.then(td => {
console.log(td);
this.todos.push(td.todo);
})
}
showEditTodo(todo:any):void{
this.todoToEdit = todo;
this.apiMessage = "";
}
EditTodo(todo:any):void{
if(!todo){ return; }
todo.id = this.todoToEdit._id;
this.todoService.updateTodo(todo)
.then(td => {
const updatedTodos = this.todos.map(t => {
if(td.todo._id !== t._id){
return t;
}
return { ...t, ...td.todo };
})
this.apiMessage = td.message;
this.todos = updatedTodos;
})
}
showDeleteTodo(todo:any):void{
this.todoToDelete = todo;
this.apiMessage = "";
}
DeleteTodo(todo:any):void{
if(!todo){ return; }
this.todoService.deleteTodo(todo)
.then(td => {
const filteredTodos = this.todos.filter(t => t._id !== td.todo._id);
this.apiMessage = td.message;
this.todos = filteredTodos;
})
}
}

Add the Delete Modal in your template:

./angular-client/src/app/todo/todo-list/todo-list.component.html

<div align="center" class="AddTodoBox" [hidden]="todoService.showAddTodoBox">
<h4>Add New Todo</h4>
<form (ngSubmit)="AddTodo(todo);todoForm.reset()" #todoForm="ngForm">
<div class="form-group">
<label for="todoText">Todo:</label>
<input type="text" [(ngModel)]="todo.todoText" #todotext="ngModel" class="form-control" name="todoText" id="todoText" required />
<div [hidden]="todotext.valid || todotext.pristine"
class="alert alert-danger">
Todo is required
</div>
</div>
<div class="form-group">
<label for="todoDesc">Description:</label>
<textarea class="form-control" [(ngModel)]="todo.todoDesc" #tododesc="ngModel" name="todoDesc" id="todoDesc" required></textarea>
<div [hidden]="tododesc.valid || tododesc.pristine"
class="alert alert-danger">
Description is required
</div>
</div>
<button type="submit" [disabled]="!todoForm.form.valid" class="btn btn-success btn-block">Submit</button>
</form>
</div>
<div *ngIf="todos.length > 0" class="TodoListBox">
<h2 align="center">Your Todo's List</h2>
<table id="mytable" class="table table-bordred table-striped">
<thead>
<th>Todo</th>
<th>View</th>
<th>Edit</th>
<th>Delete</th>
</thead>
<tbody>
<tr *ngFor="let todo of todos">
<td>{{todo.todoText}}</td>
<td><a [routerLink]="['/todo', todo._id]" title="Click to Open {{todo.todoText}}">View</a></td>
<td><p data-placement="top" data-toggle="tooltip" title="Edit"><button class="btn btn-primary btn-xs" (click) = "showEditTodo(todo)" data-title="Edit" data-toggle="modal" data-target="#edit" ><span class="glyphicon glyphicon-pencil"></span></button></p></td>
<td><p data-placement="top" data-toggle="tooltip" title="Delete"><button class="btn btn-danger btn-xs" (click) = "showDeleteTodo(todo)" data-title="Delete" data-toggle="modal" data-target="#delete" ><span class="glyphicon glyphicon-trash"></span></button></p></td>
</tr>
<tr>
</tbody>
</table>
<!-- Edit Modal -->
<div class="modal fade" id="edit" role="dialog">
<div class="modal-dialog modal-md">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h4 class="modal-title">Edit Todo</h4>
</div>
<div class="modal-body">
<div align="center" class="EditTodoBox">
<form (ngSubmit)="EditTodo(todoToEdit)" #editTodoForm="ngForm">
<div class="form-group">
<label for="todoText">Todo:</label>
<input type="text" [(ngModel)]="todoToEdit.todoText" #todoedittext="ngModel" class="form-control" name="todoText" id="todoText" required />
<div [hidden]="todoedittext.valid || todoedittext.pristine"
class="alert alert-danger">
Todo is required
</div>
</div>
<div class="form-group">
<label for="todoDesc">Description:</label>
<textarea class="form-control" [(ngModel)]="todoToEdit.todoDesc" #todoeditdesc="ngModel" name="todoDesc" id="todoDesc" required></textarea>
<div [hidden]="todoeditdesc.valid || todoeditdesc.pristine"
class="alert alert-danger">
Description is required
</div>
</div>
<button type="submit" [disabled]="!editTodoForm.form.valid || !editTodoForm.form.dirty" class="btn btn-success btn-block">Submit</button>
</form>
<div style="margin:10px;" *ngIf="apiMessage" align="center" class="alert alert-success" role="alert">
<strong>{{apiMessage}}</strong>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<!-- Delete Modal -->
<div class="modal fade" id="delete" role="dialog">
<div class="modal-dialog modal-md">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h4 class="modal-title">Delete Todo</h4>
</div>
<div class="modal-body">
<div align="center" class="DeleteTodoBox">
<div *ngIf="!apiMessage" align="center" class="alert alert-danger" role="alert">
<p>Are you sure want to delete this todo?</p>
<strong>{{todoToDelete.todoText}}</strong>
</div>
<div style="margin:10px;" *ngIf="apiMessage" align="center" class="alert alert-success" role="alert">
<strong>{{todoToDelete.todoText}}</strong> {{apiMessage}}
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" *ngIf="!apiMessage" (click)="DeleteTodo(todoToDelete)">Confirm</button>
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>

</div>
<div *ngIf="todos.length <= 0" class="NoTodosBox">
<div align="center" class="alert alert-info" role="alert">
<strong>No Todos Available in Database</strong>
</div>
</div>

Run the app again (if not running). If you click on delete button in any of your todos, you’ll see something like this:

Delete Todo Confirmation

If you will hit the confirm button, Todo will be deleted from your MongoDB and removed from your Todos List.

Now we have an CRUD application that has a Express.js backend and an Angular4 frontend. The full source for this project is hosted on Github:

--

--