Saturday, March 7, 2020

Full Stack Mini ToDo-App With Angular, HttpClient, API Controller and In-Memory Database

This tutorial is an Angular version of the plain Javascript one.



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.
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.

Steps for creating the app:

1. ng new todolist

2. 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)

3. create class

export class todomodel {

    constructor (
        public idnumber,
        public titlestring,
        public completedboolean){
            
        }
}

4. 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'

5. add various references to app.module.ts

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 { }



6. app component 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>
<!-- <router-outlet></router-outlet> -->


7. app.component.ts

import { ComponentOnInit } from '@angular/core';
import {todomodelfrom "./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 dataserviceTodoService) {
  }

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

  saveTodo(mytodotodomodel){
    // 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.idreturn -1;
          if (a.id<b.idreturn 1;
        });
        console.log('display'this.mytodolist);
      });
  }
  //deleting an existing todo
  Delete(idnumber) { // without type info
    console.log('delete'id);    
    this.dataservice.deleteTodo(id).subscribe(data =>{
        this.displayTodoList();
      });
  }
  //editing an existing todo
  Edit(eidnumber) { // 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(eidnumber) { // 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();
      });
  }

}



No comments:

Post a Comment