154

Simple to-do list, but with a delete button on list page for each item:

enter image description here

Relevant template HTML:

<tr ng-repeat="person in persons">
  <td>{{person.name}} - # {{person.id}}</td>
  <td>{{person.description}}</td>
  <td nowrap=nowrap>
    <a href="#!/edit"><i class="icon-edit"></i></a>
    <button ng-click="delete(person)"><i class="icon-minus-sign"></i></button>
  </td>
</tr>

Relevant controller method:

$scope.delete = function (person) {
  API.DeletePerson({ id: person.id }, function (success) {
    // I need some code here to pull the person from my scope.
  });
};

I tried $scope.persons.pull(person) and $scope.persons.remove(person).

Although the database deleted successfully, I can not pull this item from scope and I do not want to make a method call to the server for data the client already has, I just want to remove this one person from scope.

Any ideas?

Xsmael
  • 3,624
  • 7
  • 44
  • 60
Bye
  • 2,814
  • 7
  • 25
  • 36
  • I run this whit $route, and the view won't work fine. I always got a empty page after I did the delete :-( – zx1986 Sep 04 '13 at 11:43
  • this is not so much about deleting from scope but rather from an array, and it would be the same regardless of angular, its just javascript – Xsmael May 14 '20 at 10:17

10 Answers10

310

You'll have to find the index of the person in your persons array, then use the array's splice method:

$scope.persons.splice( $scope.persons.indexOf(person), 1 );
Joseph Silber
  • 214,931
  • 59
  • 362
  • 292
  • 49
    this is a better answer; works when the list has been filtered so that that index in the view is not the same as in the array in scope. – Andrew Kuklewicz Apr 29 '13 at 06:00
  • 5
    This is indeed the better answer. Note that in addition to the filtered lists use case mentioned by Andrew, this approach also covers the case in which you delete multiple persons and the Ajax requests for these deletes return out of order. If you have use the row indexes from before the Ajax call return, you'll end up removing the wrong rows. – Joris Jun 02 '14 at 15:44
  • 4
    Is better in some cases, but with indexOf you have to iterate over all the items to find the right one, in the Josh answer you get the index and the item faster – daver Sep 12 '14 at 14:34
  • @mike - Use [this polyfill](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf#Polyfill). – Joseph Silber Sep 18 '14 at 19:11
260

Your issue is not really with Angular, but with Array methods. The proper way to remove a particularly item from an array is with Array.splice. Also, when using ng-repeat, you have access to the special $index property, which is the current index of the array you passed in.

The solution is actually pretty straightforward:

View:

<a ng-click="delete($index)">Delete</a>

Controller:

$scope.delete = function ( idx ) {
  var person_to_delete = $scope.persons[idx];

  API.DeletePerson({ id: person_to_delete.id }, function (success) {
    $scope.persons.splice(idx, 1);
  });
};
Josh David Miller
  • 120,525
  • 16
  • 127
  • 95
  • 1
    @ScottMalachowski You're right. I forgot that part. I revised my answer to reflect that, so it'll be consistent with yours. – Josh David Miller Jan 10 '13 at 05:10
  • 13
    Careful - this index-based solution will not work if you use multiple ng-repeats of the same object in a view (e.g. Scheduled Tasks, Unscheduled Tasks, Completed Tasks all coming out of $scope.tasks) because you will have multiple items with index 2, 3, 4, etc. – shacker Mar 01 '13 at 22:20
  • Comment above, by @shacker, about multiple ng-repeats with different filtered sets of the same array, is right on. Use method below with indexOf – Andrew Kuklewicz Apr 29 '13 at 06:01
  • 4
    @AndrewKuklewicz - `indexOf` can be a more expensive operation; without filtering, it's completely unnecessary. But with filtering, `indexOf` would be the appropriate method. – Josh David Miller Apr 29 '13 at 07:04
  • I am struggling with this and had to make a minor change to the tag generation above - being - delete({{$index}}) with the {{ }} otherwise I got the string $index - BUT I have something wrong because it never calls that method. It does when I remove any mention of the index like delete() but that doesn't really help. – mikemil Oct 25 '13 at 21:43
  • @mikemil What tag generation? Do you have a plunker? – Josh David Miller Oct 25 '13 at 21:54
  • Josh David Miller - sorry, I was mistaken. I went back and tested this later and "delete($index)" is working but then I could not find the entry to delete my comment. One of the things that had me confused was when looking at the Elements in Chrome tools - the entry with delete({{$index}}) in the template, generated delete(0) for the first table row, whereas the delete($index) template generated delete($index) within my anchor tag. To me delete(0) looked correct and the 2nd didn't - like the $index was not replaced with the value in the delete() call - BUT it works like a charm! – mikemil Oct 27 '13 at 02:37
  • Be careful when using $index, here's why: http://codeutopia.net/blog/2014/11/10/angularjs-best-practices-avoid-using-ng-repeats-index/ – bluee Nov 17 '14 at 11:13
8

I would use the Underscore.js library that has a list of useful functions.

without

without_.without(array, *values)

Returns a copy of the array with all instances of the values removed.

_.without([1, 2, 1, 0, 3, 1, 4], 0, 1);
// => [2, 3, 4]

Example

var res = "deleteMe";

$scope.nodes = [
  {
    name: "Node-1-1"
  },
  {
    name: "Node-1-2"
  },
  {
    name: "deleteMe"
  }
];
    
$scope.newNodes = _.without($scope.nodes, _.findWhere($scope.nodes, {
  name: res
}));

See Demo in JSFiddle.


filter

var evens = _.filter([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });

// => [2, 4, 6]

Example

$scope.newNodes = _.filter($scope.nodes, function(node) {
  return !(node.name == res);
});

See Demo in Fiddle.

Community
  • 1
  • 1
Maxim Shoustin
  • 77,483
  • 27
  • 203
  • 225
  • I'd probably use `$scope.nodes = _.without($scope.nodes, node);` because he has reference to the `node` – jake Jun 13 '14 at 18:21
  • On modern browsers you can use `Array.prototype.filter`. `_.filter(array, fun)` becomes `array.filter(fun)`. – bfontaine Aug 11 '14 at 09:59
7
$scope.removeItem = function() {
    $scope.items.splice($scope.toRemove, 1);
    $scope.toRemove = null;
};

this works for me!

cebor
  • 6,546
  • 4
  • 24
  • 31
4

If you have any function associated to list ,when you make the splice function, the association is deleted too. My solution:

$scope.remove = function() {
    var oldList = $scope.items;
    $scope.items = [];

    angular.forEach(oldList, function(x) {
        if (! x.done) $scope.items.push( { [ DATA OF EACH ITEM USING oldList(x) ] });
    });
};

The list param is named items. The param x.done indicate if the item will be deleted.

Another references: Another example

Hope help you. Greetings.

Community
  • 1
  • 1
Drako
  • 769
  • 1
  • 8
  • 23
2

For the the accepted answer of @Joseph Silber is not working, because indexOf returns -1. This is probably because Angular adds an hashkey, which is different for my $scope.items[0] and my item. I tried to resolve this with the angular.toJson() function, but it did not work :(

Ah, I found out the reason... I use a chunk method to create two columns in my table by watching my $scope.items. Sorry!

gabn88
  • 781
  • 8
  • 23
2

You can also use this

$scope.persons = $filter('filter')($scope.persons , { id: ('!' + person.id) });
Chetann
  • 297
  • 5
  • 7
1

Angular have a built-in function called arrayRemove, in your case the method can simply be:

arrayRemove($scope.persons, person)
Ram
  • 3,092
  • 10
  • 40
  • 56
Allen
  • 316
  • 3
  • 7
1
array.splice(array.pop(item));
Taran
  • 2,895
  • 25
  • 22
0

To remove a element from scope use:

// remove an item
    $scope.remove = function(index) {
        $scope.items.splice(index, 1);
    };

From enter link description here

Cubiczx
  • 1,005
  • 11
  • 10