Full Stack Mini ToDo-App With Angular, HttpClient, API Controller and In-Memory Database
Tags: angular, tutorial, dotnet
Published: 2020-03-07 10:42:55

This tutorial is an Angular version of the plain Javascript one.(https://dev.to/zoltanhalasz/full-stack-mini-todo-app-with-javascript-ajax-api-controller-and-in-memory-database-asp-net-core-razor-pages-2bbf)

The back-end will be exactly identical, for simplicity will call the API from the previous tutorial, please study the above link for this.

The application was created from scratch, new Angular 9 application, with some inspiration from the below Angular HttpClient tutorial.

**Prerequisites:**
1. you must have Angular 9 (or 7,8) installed on your PC
2. basic / intermediate javascript/typescript
3. review above mentioned tutorials. Web api for back-end is identical.
4. the front-end, styling belong to original tutorial from Dennis Ivy, I just transposed here to Angular. https://youtu.be/hISSGMafzvU
5. basic tutorial for Angular, which teaches some of the steps: https://www.positronx.io/angular-7-httpclient-http-service/

Application is live under: https://angulartodo.zoltanhalasz.net/

Zip Repo: here. https://drive.google.com/open?id=1CvS8G3Hj9g-HJgQjTyCXW6nkA4j_ff5M

Steps for creating the app:

*a. ng new todolist*

*b. styling*
add bootstrap: 
```
npm install bootstrap
```

css file include it in Angular.json
```
"styles": [
  "node_modules/bootstrap/dist/css/bootstrap.min.css",
  "styles.scss"
]
```

in the index html file, I will include some special fonts in the head
```
 <link href="https://fonts.googleapis.com/css?family=Montserrat&amp;display=swap" rel="stylesheet">
```

app.component.css will include the formatting from Dennis Ivy's original tutorial.(see repo)

*c. create class*

```
export class todomodel {
    constructor (
        public id: number,
        public title: string,
        public completed: boolean){           
        }
}
```
*d. create service* - I will not copy the lengthy code here.(see repo)
Some starting points: it is a very close copy of the api service from this tutorial (also mentioned above):  https://www.positronx.io/angular-7-httpclient-http-service/

The Api link reference will be identical with the plain javascript tutorial.
```
'https://todolist.zoltanhalasz.net/api/TodoModels'

```
*e. add various references to app.module.ts*

```js
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule }   from '@angular/forms';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HttpClientModule } from '@angular/common/http';
import { TodoService } from './todo.service';

@NgModule({
  declarations: [
    AppComponent
  ],

  imports: [
    BrowserModule,
    AppRoutingModule,
    FormsModule,
    HttpClientModule
  ],
  providers: [TodoService],
  bootstrap: [AppComponent]
})

export class AppModule { }
```
f.app component html
```html

<div class="container">
  <div id="task-container">
      <div id="form-wrapper">
          <form id="form" (ngSubmit)="onSubmit()" #todoForm="ngForm">    
                  <div class="flex-wrapper">
                    <div style="flex: 6">                      
                      <input type="text" class="form-control" id="title" required [(ngModel)]="mytodo.title" name="title"
                      #title="ngModel">
                    </div>            
                    <div style="flex: 1">
                      <button type="submit" id="submit" class="btn">Submit</button>     
                    </div>      
                  </div>                     
          </form>
      </div>
      <div id="list-wrapper">
        <div class="task-wrapper flex-wrapper" *ngFor="let item of mytodolist">
          <div style="flex:7">
            <span [className]="item.completed ? 'finish-title' : 'title'"      
            (click)="FinishTodo(item.id)">{{item.title}}</span>
          </div>
          <div style="flex:1">
            <button class="btn btn-sm btn-outline-info edit" (click)="Edit(item.id)">Edit </button>
          </div>
          <div style="flex:1">
            <button class="btn btn-sm btn-outline-danger delete" (click)="Delete(item.id)">Delete</button>
          </div>
        </div>
      </div>
  </div>
</div>
```
*g. app.component.ts*

```js
import { Component, OnInit } from '@angular/core';
import {todomodel} from "./todomodel";
import { TodoService } from './todo.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})

export class AppComponent implements OnInit{

  title = 'todolist';

  // when a task is being edited  

  edited = false;

  // initialize sample todo

  mytodo = new todomodel(0,'',false);

  // this array will always store list of todos retrieved from server

  mytodolist:  todomodel [];

  //injecting the dataservice

  constructor (private dataservice: TodoService) {
  }

  // submitting the form 
  onSubmit() {      
      this.saveTodo(this.mytodo);
      // resetting the mytodo value
      this.mytodo = new todomodel(0,'',false);
    }

  saveTodo(mytodo: todomodel){
    // if it is not an editing
    if (!this.edited) {
      if (this.mytodo.title=='') return;
      // saving new todo
        this.dataservice.createTodo(mytodo).subscribe(data=> {

          this.displayTodoList();

      });
    }
    // if we are editing an existing todo
    else {
      this.edited=false;
      console.log('this is being edited',mytodo);
            // update existing todo
      this.dataservice.updateTodo(this.mytodo.id,this.mytodo).subscribe(data =>
        {     
          this.displayTodoList();
        }       
        );
    }    
  }

  ngOnInit(){
    this.displayTodoList();
  }
  //this function retrieves the whole array of todos from server, using api service injected
  displayTodoList() {
    this.dataservice.getTodoList().subscribe(data =>
      {

        // as the Web Api doesn't sort the list of todos, we do here in the frontend
        this.mytodolist = data.sort((a,b)=> {
          if (a.id>b.id) return -1;
          if (a.id<b.id) return 1;
        });
        console.log('display', this.mytodolist);
      });
  }

  //deleting an existing todo

  Delete(id: number) { // without type info
    console.log('delete', id);    
    this.dataservice.deleteTodo(id).subscribe(data =>{
        this.displayTodoList();
      });
  }
  //editing an existing todo
  Edit(eid: number) { // without type info
    console.log('editing',eid);
    this.mytodo = this.mytodolist.filter(x=>x.id ==eid)[0];
    this.edited = true;   
  }
    //finalizing(crossing) an existing todo
  FinishTodo(eid: number) { // without type info
    // console.log('finishing', eid);   
    const mytodofinished = this.mytodolist.filter(x=>x.id ==eid )[0];
    mytodofinished.completed =  !mytodofinished.completed ;
    //calling the update observable
    this.dataservice.updateTodo(eid,mytodofinished).subscribe(data =>{     
        this.displayTodoList();
      });
  }
}
```