1

I'm iterating over an array of objects via ngRepat. Each one gets a directive. Inside that directive I'm trying to assign to the scope variable which at first works fine, but upon changing the array it gets resetted.

<test-directive obj="obj" ng-repeat="obj in objects"></test-directive>

app.directive('testDirective', function() {
    return {
    restrict: 'E',
    scope: { obj: '=' },
    template: '<div><a ng-click="doTheThing()">{{obj.title}}</a></div>',
    link: function($scope, $element, $attrs) {
        $scope.doTheThing = function() {
        $scope.obj = { changed: true, title: 'Is changed.'};
      };
    }
  };
});

Demo: https://jsfiddle.net/byjvey2b/ (Click the items to apply the change, upon clicking verify which adds a new element, everything is reset).

Simply changing the attributes accordingly, instead of the whole object, won't help me, I'm actually reassigning different subclasses in my own code.

tom
  • 692
  • 6
  • 12

3 Answers3

2

This works: https://jsfiddle.net/ft8qgccf/

app.directive('testDirective', function() {
    return {
    restrict: 'E',
    scope: { obj: '=' },
    template: '<div><a ng-click="doTheThing()">{{obj.title}}</a></div>',
    link: function($scope, $element, $attrs) {
        $scope.doTheThing = function() {
        $scope.obj.title = 'Is changed';
      };
    }
  };
});

By doing $scope.obj.title = 'Is changed'; instead of $scope.obj = { changed: true, title: 'Is changed.'}; you are taking advantage of "the dot".

Basically, "the child scope uses prototypical inheritance to look up the value, so as long as it never gets set on the child, then it will defer to the parent scope". So, by changing the value of obj in the child scope, you are breaking the connection to the parent scope. Read about it here.

danyamachine
  • 1,848
  • 1
  • 18
  • 21
  • As I said, I can't actually only change attributes since I have to assign a new subclass, otherwise it would be as trivial as shown by you, yes. – tom Jun 03 '16 at 21:41
  • could you elaborate on that – danyamachine Jun 03 '16 at 21:51
  • I have tracks from different sources, a common Track super class and LocalTrack, SomeSourceTrack etc subclasses. The directive serves to pick a track, so its track property may change between classes (as you can change the source at any time). So just changing a bunch of fields on an object won't do it for me, unless I also change prototype etc – tom Jun 03 '16 at 23:19
1

I think this is intended behavior. The ng-repeat still loops for "obj in objects", and the data will refresh at many random intervals depending on angular's cycles. Here, you can see it refreshing the data everytime you add a new item. You are not changing the "objects" data, only the local reference to the item in the objects array.

Try passing the $index via ng-repeat to the item local scope,

<a ng-click="doTheThing($index)">

Then update the data

$scope.objects[index] = { changed: true, title: 'Is changed.'};

https://jsfiddle.net/byjvey2b/5/

internet-nico
  • 1,637
  • 19
  • 17
  • Any idea how to keep it more general-purpose other than adding a index and a list property to my directive? Other than that it works, thanks! – tom Jun 03 '16 at 21:40
  • this answer works on the same premise as mine - an important basic fact about angular inheritance. he's suggested you modify `$scope.objects[0]` which is effectively `$scope.objects.0`, hence "the dot" – danyamachine Jun 03 '16 at 21:50
0

You can call a method in the controller to replace the original object with the new one.

<test-directive obj="obj" updated="replaceObject(obj, newObj)" ng-repeat="obj in objects">
</test-directive>

In controller:

$scope.replaceObject = function(object, newObject) {
    var i = $scope.objects.indexOf(object);
    $scope.objects.splice(i, 1, newObject);
}

In directive:

scope: { 
    obj: '=',
    updated: '&'  
},
template: '<div><a ng-click="doTheThing()">{{obj.title}}</a></div>',
link: function($scope, $element, $attrs) {
    $scope.doTheThing = function() {
        $scope.updated({newObj: { changed: true, title: 'Is changed.'}});
  };
}

https://jsfiddle.net/j2mexyj0/2/

meum
  • 112
  • 7