Beardslapper Beardslapper - 3 months ago 14
AngularJS Question

Watch not triggered when service variable is updated in Angular

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.

Answer

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.

Consider using UI.Sortable maintained by Angular UI team if you are using jQuery based library.

Comments