Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

JavaScript Building a MEAN Application Creating and Editing Data in a MEAN App Create and Update Data in Angular

Delete Data in Angular

So I finished the MEAN course and enjoyed it. I noticed we never covered deleting things out of mongoDB with mongoose. I was going to tackle this but was curious to know if anyone else has done this. What was your approach?

2 Answers

Ken Howard
STAFF
Ken Howard
Treehouse Guest Teacher

You'll need to add a DELETE route on the apiRouter in express (in src/api/index.js)

apiRouter.delete('/todos/:id', function (req, res) {
    var todoId = req.params.id; // This maps to the :id in the url
    Todo.findByIdAndRemove(todoId, function (err, result) {
        if (err) {
            res.status(500).json({ message: err.message });
        } else {
            res.json({ message: 'Deleted Todo' });
        }
    });
});

Once the DELETE route is created you can use postman to send a DELETE HTTP request.. but you'd probably want to configure angular to do the delete when the user clicks the button. The button is already configured to fire the deleteTodo method on dataService. You just need to modify that a bit... (app/services/data.js)

this.deleteTodo = function(todo) {
    if (!todo._id) {
        return $q.resolve();
    }
    return $http.delete('/api/todos/' + todo._id).then(function () {
        console.log("I deleted the " + todo.name + " todo!"); 
    });
};

The way the TodoCtrl is currently set up is to remove the todo from the list before it calls the deleteTodo method on dataService. This is OK, but what if the server request fails? Change the deletedTodo method on the controller to look like this:

$scope.deleteTodo = function (todo, index) {
    dataService.deleteTodo(todo).success(function () {
        $scope.todos.splice(index, 1);
    });
});

When the request is successful then we can safely remove the todo from the list.

I hope this helps. Let me know if you have any trouble implementing this.

I implemented your method which was close to what I was attempting to do

I am recieving this output in the console when i delete an item (it DOES delete the items though)

TypeError: dataService.deleteTodo(...).success is not a function
    at Scope.$scope.deleteTodo (http://localhost:3000/scripts/todo.bundle.js:67:35)
    at fn (eval at <anonymous> (http://localhost:3000/scripts/vendor.bundle.js:14201:16), <anonymous>:4:418)
    at expensiveCheckFn (http://localhost:3000/scripts/vendor.bundle.js:15191:19)
    at callback (http://localhost:3000/scripts/vendor.bundle.js:24661:18)
    at Scope.$eval (http://localhost:3000/scripts/vendor.bundle.js:16935:29)
    at Scope.$apply (http://localhost:3000/scripts/vendor.bundle.js:17035:26)
    at HTMLAnchorElement.<anonymous> (http://localhost:3000/scripts/vendor.bundle.js:24666:24)
    at defaultHandlerWrapper (http://localhost:3000/scripts/vendor.bundle.js:3513:12)
    at HTMLAnchorElement.eventHandler (http://localhost:3000/scripts/vendor.bundle.js:3501:10)

ReferenceError: todos is not defined
    at todo.bundle.js:110
    at processQueue (vendor.bundle.js:15667)
    at vendor.bundle.js:15683
    at Scope.$eval (vendor.bundle.js:16935)
    at Scope.$digest (vendor.bundle.js:16751)
    at Scope.$apply (vendor.bundle.js:17043)
    at done (vendor.bundle.js:11381)
    at completeRequest (vendor.bundle.js:11579)
    at XMLHttpRequest.requestLoaded (vendor.bundle.js:11520)

Here are my files

api\index.js
'use strict';

var express = require('express');
var Todo = require('../models/todo');

var router = express.Router();

router.get('/todos', function(req, res) {
    Todo.find({}, function(err, todos) {
        if (err) {
            return res.status(500).json({message: err.message });
        }
        res.json({todos: todos });
    });

});

router.post('/todos', function(req, res) {
    var todo = req.body;
    Todo.create(todo, function(err, todo) {
        if (err) {
            return res.status(500).json({err: err.message});
        }
        res.json({'todo': todo, message: 'todo has been created'});
    }); 
});

router.put('/todos/:id', function(req, res){
    var id = req.params.id;
    var todo = req.body;
    console.log(req.body);
    if (todo && todo._id !== id) {
        return res.status(500).json({err: 'IDs do not match.'});
    }
    Todo.findByIdAndUpdate(id, todo, {new: true}, function(err, todo){
        if (err) {
            return res.status(500).json({err: err.message });
        }
        res.json({'todo': todo, message: 'todo has been updated.'});
    }); 
});

router.delete('/todos/:id', function (req, res) {
    var todoId = req.params.id;
    Todo.findByIdAndRemove(todoId, function (err, result) {
        if (err) {
            res.status(500).json({ message: err.message });
        } else {
            res.json({ message: 'Deleted Todo' });
        }
    });
});

module.exports = router;
services\data.js
'use strict';

var angular = require('angular');


angular.module('todoListApp')
.service('dataService', function($http, $q) {
  this.getTodos = function(cb) {
    $http.get('/api/todos').then(cb);
  };

  this.deleteTodo = function(todo) {
    if (!todo._id) {
        return $q.resolve();
    }
    return $http.delete('/api/todos/' + todo._id).then(function () {
        console.log("I deleted " + todos.length + " todos!"); 
    });
  };

  this.saveTodos = function(todos) {
    var queue = [];
    todos.forEach(function(todo){
        var request;
        if (!todo._id) {
            request = $http.post('/api/todos', todo)
        } else {
            request = $http.put('/api/todos/' + todo._id , todo).then(function(result){
                todo = result.data.todo;
                return todo;
            })
        };
        queue.push(request);
    });
    return $q.all(queue).then(function(results) {
        console.log("I saved " + todos.length + " todos!");
    });
  };
});
controllers\todo.js
'use strict';

var angular = require('angular');

angular.module('todoListApp')
.controller('todoCtrl', function($scope, dataService) {

  $scope.saveTodos = function() {
    var filteredTodos = $scope.todos.filter(function(todo){
      if(todo.edited) {
        return todo
      };
    })
    dataService.saveTodos(filteredTodos).finally($scope.resetTodoState());
  }; 

  $scope.resetTodoState = function() {
    $scope.todos.forEach(function(todo){
      todo.edited = false;
    });
  };

  $scope.deleteTodo = function (todo, index) {
    dataService.deleteTodo(todo).success(function () {
        $scope.todos.splice(index, 1);
    });
  };
});

Also when I complete and item and save it tells me it saves and the JSON reflects that Completed:true. However on page refresh that item is back to completed:false.

Ken Howard
Ken Howard
Treehouse Guest Teacher

John Weland ... replace the .success method with .then method and it should work. The .success method is only available on a raw promise. Sorry for the bad code.

No worries mate, changed it and it works like a charm. Now once I get the completed status to save, I'll be golden.

Plan is to extend this out to allow todo items to have categories. But one thing at a time I suppose. Thanks again.

I'm implementing this, but getting a 500 internal server error when I use it. Any insights?

I didnt implement this part

$scope.deleteTodo = function (todo, index) {
    dataService.deleteTodo(todo).success(function () {
        $scope.todos.splice(index, 1);
    });
});

And it worked for me... But still when I mark todo as done its not presist

Dobrea Vladislav
Dobrea Vladislav
14,141 Points

I have found why it does not persist, it is because in templates/todo.html you probably have

ng-init="todo.completed = false"

which overrides your persistent data from database;

Dobrea Vladislav I noticed that too but I'm unsure how to fix it because removing that seems to no initialize things to false when you add new things.

Dobrea Vladislav NVM I removed it with webpack running and it works... odd...

Daniel Cudney
Daniel Cudney
4,515 Points

add this to main.js in controllers after you remove (ng-init="todo.completed = false") frin templates/todos.html

$scope.addTodo = function() {
  console.log("Added");
    var todo = {name: "Become the best version of you.", completed: false};
    $scope.todos.unshift(todo);
  };
Daniel Cudney
Daniel Cudney
4,515 Points

add this to main.js in controllers after you remove (ng-init="todo.completed = false") frin templates/todos.html

$scope.addTodo = function() {
  console.log("Added");
    var todo = {name: "Become the best version of you.", completed: false};
    $scope.todos.unshift(todo);
  };