29

I basically want the equivalent to binding to 'add' and 'remove' events in Backbone's Collections. I see basically no way of doing this in AngularJS, and the current workaround we've settled for is $watch()ing the array's length and manually diffing/recalculating the whole thing. Is this really what the cool kids do?

Edit: Specifically, watching the array's length means I don't easily know which element has been changed, I need to manually "diff".

6
  • 1
    Is there anything specific you wish to do? What is wrong with $watching the array's length? Commented Mar 28, 2013 at 12:38
  • @ganaraj Edited, I just want to know which element got changed Commented Mar 28, 2013 at 12:44
  • Angular's data binding does work like events; what you need to do is to change the way you are thinking. Why do you actually need to know that? Commented Mar 28, 2013 at 16:24
  • 2
    @ganaraj watching length is not safe. There can be multiple changes in the array being watched in the same digest cycle, making the watcher not fire at all. e.g. array.splice(0,1,"hello") Commented Mar 28, 2013 at 16:26
  • @fastreload e.g. I have a list of stuff (say, todos) and the server is waiting to receive notifications of which were deleted/added Commented Mar 28, 2013 at 16:31

5 Answers 5

37

I think using $watch is a good solution, but $watchCollection can be better for you. $watchCollection doesn't perform deep comparison and just watchs for array modification like insert, delete or sort (not item update).

For exemple, if you want to keep an attribut order synchronize with the array order :

$scope.sortableItems = [
    {order: 1, text: 'foo'},
    {order: 2, text: 'bar'},
    {order: 3, text: 'baz'}
];

$scope.$watchCollection('sortableItems', function(newCol, oldCol, scope) {
    for (var index in newCol) {
        var item = newCol[index];
        item.order = parseInt(index) + 1;
    }
});

But for your problem, I do not know if there is a better solution than manually browse the array to identify the change.

Sign up to request clarification or add additional context in comments.

Comments

12

The way to watch an array in Angular is $watch(array, function(){} ,true)

3 Comments

That is a deep watch. You probably dont need that in most of the cases.
@Jason Als: Thanks, but that doesn't tell me which element has been removed/added. I edited the question to make that clearer :)
@ganaraj actually you do, see my comment on op's question
3

I would create child scopes and watch them individually. here is an example:

$scope.myCollection = [];
var addChild = function()
{
  var Child = $scope.$new();
  Child.name = 'Your Name here';

  Child.$watch('name', function(newValue) {
     // .... do something when the attribute 'name' is changed ...
  });

  Child.$on('$destroy', function() {
    //... do something when this child gets destroyed 
  });


  $scope.myCollection.push(Child); // add the child to collection array

};

// Pass the item to this method as parameter, 
// do it within an ngRepeat of the collection in your views 
$scope.deleteButtonClicked = function(item)
{
  var index = $scope.myCollection.indexOf(item); //gets the item index
  delete $scope.myCollection[index]; // removes the item on the array
  item.$destroy(); // destroys the original items
}

Comments

0

Please tell more about your usecase. One of the solutions of tracking element persistance is using ngRepeat directive with custom directive that listening element's $destroy event:

<div ng-repeat="item in items" on-delete="doSomething(item)">

angular.module("app").directive("onDelete", function() {
    return {
        link: function (scope, element, attrs) {
            element.on("$destroy", function () {
                scope.$eval(attrs.onDelete);
            });
        }
    }
});

2 Comments

That's nice but I just don't feel like making my MVC bind to the elements in the view to know when something in my model has changed is the right way to go...?
Yes. Another solution is creating Model and Collection classes like in Backbone.
0

Perhaps the solution is to create the collection class ( like backbone does ) and you can hook into events pretty easily as well.

The solution I have done here isnt really comprehensive, but should give you a general guidance on how this could be done perhaps.

http://beta.plnkr.co/edit/dGJFDhf9p5KJqeUfcTys?p=preview

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.