Shaikh Sakib
Shaikh Sakib

Reputation: 109

Cannot read property '_id' of undefined after confirming delete In MEAN CRUD app?

enter link description here

Here is the link of the tutorial I have been using to learn MEAN CRUD operations.

As requested I am posting the relevant code below. Also the Chrome console says TypeError: Cannot read property '_id' of undefined at todo-list.component.ts:56.

todolist.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;
                   })
 }
}

and here is 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>
    <input type="text" class="form-control" [(ngModel)]="todo.todoDesc" #tododesc="ngModel" name="todoDesc" id="todoDesc" required />
    <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 && 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>

Upvotes: 1

Views: 345

Answers (1)

HMR
HMR

Reputation: 39270

As stated in the comment; you can solve it by using the todo item you are deleting. It is available through closure.

You could look at what todoService.deleteTodo resolves to and see if the id of the deleted todo item is available there.

To use the id of the deleted todo using the todo in closure you can do:

const filteredTodos = this.todos.filter(t => t._id !== todo._id)

Upvotes: 1

Related Questions