AngularJS: Force ng-repeat update when property of an element changes

Mark Olbert picture Mark Olbert · May 15, 2015 · Viewed 7.4k times · Source

I'm wrestling with the way angular watches arrays. I have the following markup:

<map name="theMap" center="{{mapInfo.center.toUrlValue()}}" zoom="{{mapInfo.zoom}}" on-dragend="dragEnd()" on-zoom_changed="zoomChanged()">
    <marker ng-repeat="pin in pins() track by pin.iconKey()" position="{{pin.latitude}}, {{pin.longitude}}" title="{{pin.streetAddress}}" pinindex="{{$index}}" on-click="click()" 
            icon="{{pin.icon()}}"></marker>
</map>

Each individual pin returned by pins() has a number of properties, sub-properties, etc. One of those sub-properties controls the marker color. When that subproperty changes I want the UI to update.

Because ng-repeat appears to $watch based on simply changes to the collection it's not obvious how to achieve that. I thought that my tracking function, iconKey(), would do it because it returns a different value depending upon the subproperty's value. But that didn't work.

One other thing: the subproperty gets changed in the callback from an $interval that runs under a directive. I mention this because, in an earlier post, someone thought that there might be a context/scope problem.

However, even when I make the change in an event listener within the UI's controller (where the event is raised within the "success" clause of the $interval callback) it still doesn't work.

That's why I think the problem is just angular not noticing the change in iconKey(); it's acting like all it $watches for ng-repeat is the array's length. Which doesn't change when the subproperty changes.

Update

I've created a plunker to demonstrate the issue I'm facing. You can find it at http://plnkr.co/edit/50idft4qaxqw1CduYkOd

It's a cut down version of the app I'm building, but it contains the essential elements (e.g., a data context service to hold information about the map pins and an $interval service to toggle the subproperty of one of the pin array elements).

You start the update cycle by clicking Start in the menu bar (you may want to drag the map down slightly to put both pins into full view). It should toggle the color of each pin, alternatively, 5 times each, once every 2 seconds. It does this by toggling the value of the isDirty property of the pin object in a listener defined in the controller. The event is raised by the $interval service.

If you break on line 22 during the test cycle you'll see the pin's icon being returned. So something within angular is calling for the information...but the pin color doesn't change.

I look forward to someone quickly pointing out the bone-headed mistake that has nothing to do with any of my theories :). Apologies in advance for whatever blinders I'm wearing.

Update 2

After checking out the code snippet in the response I simplified my plnkr and demonstrated that angular is, in fact, updating the UI when a subproperty changes. So this appears to be a limitation or bug in ng-map.

Answer

Kanak Singhal picture Kanak Singhal · May 15, 2015

What you are missing here is the concept of array and function your function pins() passes an array and that array is been bound with ng-repeat. But the brutal fact here is that no matter what that array is never changed, because you do not have ANY reference to that array hence the rg-repeat will always remain as is...

I'll suggest to try get the array be referenced two ways to do that

ng-init="pinsArray = pins()"

and second inside controller

$scope.pinsArray = $scope.pins()

then make changes to $scope.pinsArray inside controller

ng-repeat will be changed to

ng-repeat="pin in pinsArray"

also read about filters I am guessing that's what you where trying to do with "track by"

hope this helps..

Edit: different story with ngMap markers

seems like it doesn't watch sub-property. so here's a work around

add following statement to you update the pinsArray after making changes to its properties.

pinsArray = angular.copy(pinsArray);

the solved plnkr example: http://plnkr.co/edit/EnW1RjE9v47nDpynAZLK?p=preview