So I keep reading that jQuery manipulation from within a Controller is bad practice, but I'm not clear on why, or how to correct.
Below is code from a Youtube tutorial which even the video creator comments is a bad idea, but doesn't explain why and continues to use the bad behavior anyway.
From https://www.youtube.com/watch?v=ilCH2Euobz0#t=553s :
$scope.delete = function() {
    var id = this.todo.Id;
    Todo.delete({id: id}, function() {
        $('todo_' + id).fadeOut();
    });
};
SOLUTION:
Based on Langdon's answer below, I've arrived at the following working code for my own work, which derives slightly from the example code above:
var ProjectListCtrl = function ($scope, Project) {
    $scope.projects = Project.query();
    $scope.delete = function() {
        var thisElem = this;
        var thisProject = thisElem.project;
        var id = thisProject.id;
        Project.delete({id: id}, function() {
            var idx = $scope.projects.indexOf(thisProject);
            if (idx !== -1) {
                thisElem.destroy('removeItem('+idx+')');
            }
        });
    }
    $scope.removeItem = function(idx) {
        $scope.projects.splice(idx, 1);
    }
}
app.directive('fadeOnDestroy', function() {
    return function(scope, elem) {
        scope.destroy = function(funcComplete) {
            elem.fadeOut({
                complete: function() {
                    scope.$apply(funcComplete)
                }
            });
        }
    }
});
This differs from Langdon's answer in a few ways. I wanted to avoid adding a parameter to the ngClick callback, so I'm storing it in thisProject. Also, the example and my code needs to call destroy from within a $http success callback so instead of this which is no longer relevant, I'm storing the clicked element in thisElem.
UPDATE 2:
Updated my solution further to reflect that funcComplete was not actually modifying the original $scope.
 
     
    