Reputation: 153
So, basically what I'm doing is loading the data in the view using ng-repeat
. When a drag and drop occurs, I'm moving the item from one group to another using a directive. The directive calls the move function in the service and updates the list variable.
My problem is that when the item is moved, the ng-repeat
is not updated. I tried using $scope.$watch
, but with no luck. The service list variable is being updated, but the watch in not being triggered for some reason. I tried using broadcast
, which worked, but I've read that it's a bad practice to use broadcast in the controller as it creates bugs.
What is the best way to update the ng-repeat
? Not sure why it isn't working. If you require more details, please let me know.
This is my service
angular.module('Data', [])
.factory('DataService', DataService);
function DataService() {
var list = [];
list.push({
id: 6,
title: "First Group",
items: [
{
id: 1,
title: "This is an item"
}
]
});
list.push({
id: 7,
title: "Testng",
items: [
]
});
return {
'get': get,
'move': move,
};
function get() {
return list;
}
function move(index, fromItemIndex, toItemIndex) {
list[fromItemIndex].items.push({
id: 5,
title: "This is an item"
});
}
}
This is my controller
function MyController($scope, DataService) {
var vm = this;
vm.list = DataService.get();
$scope.$watch(function() {
return DataService.get();
}, function(value) {
console.log('Wtatching');
console.log(value);
}, true);
}
My View
<div ng-repeat="item in vm.list track by $index">
<div class="group" droppable group="<% $index %>">
<div class="group-title">
<% group.title %>
</div>
<div class="group-content">
<div ng-repeat="item in item.items track by $index">
<div class="group-item">
<div class="group-item-title"
draggable
group="<% $parent.$index %>"
item="<% $index %>">
<% item.title %>
</div>
</div>
</div>
</div>
</div>
</div>
EDIT
Here is a plnker version: https://plnkr.co/edit/bQuotNU7oOm92PCgmhcj
When you drag item 1 and drop it onto itself, you will see that the service list is update, but the watch is not triggered.
Upvotes: 2
Views: 81
Reputation: 25797
I think the problem is with your ng-repeat
expression. Try removing the track by $index
.
If you are using jQuery (or other non-angular callback) for moving items (drag-and-drop), then you need to wrap your move
code like this:
function move(index, fromItemIndex, toItemIndex) {
$timeout(function() {
list[fromItemIndex].items.push({
id: 5,
title: "This is an item"
});
});
}
Make sure you inject $timeout
in your factory. There is a concept of digest cycle in Angular where Angular regularly update the data within the Angular context.
From the docs:
In the $digest phase the scope examines all of the $watch expressions and compares them with the previous value. This dirty checking is done asynchronously.
If some Angular data modified in a non-angular context like the jQuery drag & drop, the Angular will be unaware of that change until you tell it. So using $scope.$apply()
is used to tell Angular that something has changed. But $apply()
sometimes fails if digest cycle is already in progress so using some wrapped service of Angular like $timeout
is recommended.
The gist:
The
$timeout
is used to implicitly trigger a digest cycle
More detail here: https://docs.angularjs.org/error/$rootScope/inprog#triggering-events-programmatically
(Consider using UI.Sortable maintained by Angular UI team if you are using jQuery based library.)
Upvotes: 2