Reputation: 992
I have a list displayed with ng-repeat and ordered by name desc.
Each item has a item-delete
directive on it that holds the $index
and on item click I delete the item from the list using given index
with splice
function and then I update the list - which doesn't work!
From my debugging results that the problem occurs after orderBy
filter is called inside directive. If I directly remove the item like this scope.list.splice(index, 1)
it works, but the removed item is not the correct one as items are ordered by ng-repeat, so I have to order them the same way and after that I can perform a correct deletion.
A workaround is to $emit
the new list and update the scope inside controller, but I don't wanna do this. Tested & working (see snippet).
A scope.$apply
will run into a digest already in progress
error (because this part of code is inside a promise in my app) & without $timeout
the list won't update when it's working (with noFilter
filter, for eg.).
*I'm using a directive to remove a item from list because I'm doing a lot more on item click (DOM changes, service calls) and the controller isn't the place for that (in case you're thinking why I'm not doing this through controller).
Also, a plnkr here.
// JS
var APP = angular.module('APP', []);
APP.controller('Home', function($scope, $filter){
var objData = {
"1": { id: 1, name: "Abc" },
"2": { id: 2, name: "Bbc" },
"3": { id: 3, name: "Fea" },
"4": { id: 4, name: "Dbc" }
};
$scope.list = $filter('objToArr')(objData);
//part of workaround...
$scope.$on('listUpdate', function(evt, data){
$scope.list = data;
})
});
APP.directive('itemDelete', function($filter, $timeout){
return{
restrict: 'A',
link: function(scope, el, attrs){
var delIndex;
attrs.$observe('itemDelete', function(val){
delIndex = val;
});
angular.element(el).click(function(){
console.log('deleting item index '+ delIndex);
console.log('list length before ' + scope.list.length);
//delete item from ng-repeat ordered list
$timeout(function(){
var listOrdered = $filter('orderBy')(scope.list, '-name'); //replace 'orderBy' with 'noFilter' -> it works, but removes wrong item!
//var listOrdered = scope.list; // uncomment & comment line before this -> it works, but removes wrong item!
listOrdered.splice(delIndex,1);
scope.list = listOrdered;
//workaround...
//scope.$emit('listUpdate', scope.list);
console.log('list length after ' + scope.list.length);
});
});
}
}
})
APP.filter('objToArr', function(){
return function(input){
var arrData = [];
angular.forEach(input, function(arr, key){
arrData.push(arr);
});
return arrData;
}
});
APP.filter('noFilter', function(){
return function(input){
return input;
}
});
ul li{ display: block; margin-bottom: 4px; background-color: #ccc; padding: 5px; }
ul{ list-style:none; }
<!DOCTYPE html>
<html ng-app="APP">
<head>
<script data-require="[email protected]" data-semver="2.1.3" src="http://code.jquery.com/jquery-2.1.3.min.js"></script>
<script src="https://code.angularjs.org/1.3.15/angular.js" data-semver="1.3.15" data-require="[email protected]"></script>
</head>
<body ng-controller="Home">
<h4>Click on item - remove from list</h4>
<ul>
<li item-delete="{{ $index }}" ng-repeat="item in list | orderBy:'-name'">{{ item.name }}</li>
</ul>
</body>
</html>
Upvotes: 1
Views: 1069
Reputation: 5825
The problem is in this line:
scope.list = listOrdered;
That line define a new list
property on the chlid scope instead of changing the actual list, which is a property of the parent scope. (so doing scope.$parent.list = listOrdered works fine..).
You can solve it by putting the list in an object, which will keep the referneces in sync:
$scope.objList = { list: $filter('objToArr')(objData) };
var listOrdered = $filter('orderBy')(scope.objList.list, '-name'); //replace 'orderBy' with 'noFilter' -> it works, but removes wrong item!
listOrdered.splice(delIndex,1);
scope.objList.list = listOrdered;
In your HTML:
<li item-delete="{{ $index }}" ng-repeat="item in objList.list | orderBy:'-name'">{{ item.name }}</li>
Check this plunker
Upvotes: 1