Reputation: 2305
I'm having some trouble understanding why if I use track by Index in a ng-repeat, after removing a item from a array I get the "wrong" values in the scope of a directive.
As you can see in the below plunker if I remove the first Item in the array tracked by index, the value in the directive scope will not be the initial one.
I was expecting to have after the removal: 2 - b 3 - c
but I get 2 - a 3 - b
If I don't use track by index I get the right values.
This is the Script
nsTests.controller('nsTestCtrl', ['$rootScope', '$scope', '$filter', '$timeout',
function ($rootScope, $scope, $filter, $timeout) {
$scope.data = [
{
id: 1,
name: 'a'
},
{
id: 2,
name: 'b'
},
{
id: 3,
name: 'c'
},
];
$scope.data2 = [
{
id: 1,
name: 'a'
},
{
id: 2,
name: 'b'
},
{
id: 3,
name: 'c'
},
];
$scope.removeByIndex = function (index) {
$scope.data.splice(index, 1);
}
$scope.removeByItem = function (item) {
var index = $scope.data2.indexOf(item);
if (index !== -1) {
$scope.data2.splice(index, 1);
}
}
}
]);
nsTests.directive('test', function ($compile) {
return {
restrict: 'E',
template: '{{value}}',
scope: {
data: '='
},
controller: function ($scope) {
$scope.value = $scope.data.name;
}
};
});
Here is a plunker with the problem
Upvotes: 1
Views: 3703
Reputation: 315
using track by index, angular will reuse nodes
This is effectively the issue; the directive is not reinitialised as Angular seems ignore it's value parameters and keep the original data from the index.
E.g.
var arr = [
{ id: 0, status: "Pass" },
{ id: 1, status: "Fail" }
];
If you repeat this data and have a custom directive in the repeat that would display a green dot for pass and a red dot for fail.
Example: https://plnkr.co/edit/ytQ9p6YJOfrDubCGIc2N?p=preview
.pass {
color: green;
}
.fail {
color: red
}
.pass,
.fail {
font-size: 24px;
}
<table>
<th>ID</th>
<th>Status</th>
<tr>
<td>1</td>
<td class="pass">▪</td>
</tr>
<tr>
<td>2</td>
<td class="fail">▪</td>
</tr>
</table>
When applying a filter e.g. via $filter on arr
that filters out the the object with ID:1
.pass {
color: green;
}
.fail {
color: red
}
.pass,
.fail {
font-size: 24px;
}
<table>
<th>ID</th>
<th>Status</th>
<tr>
<td>2</td>
<td class="pass">▪</td>
</tr>
</table>
As you can see, 1 was filtered out, but the result is now incorrect. This is because Angular is being a little lazy and not reinitialising the directive that we are using even though the data we passed in is now different.
Upvotes: 0
Reputation: 4274
I think my plnkr and explanation will help you to understand these thing better way.
Here issue is not with the track by $index
nsTests.directive('test', function ($compile) {
return {
restrict: 'E',
template: '{{value}}',
scope: {
data: '='
},
controller: function ($scope) {
$scope.value = $scope.data.name;
}
};
});
You have created isolated scope for you test directive and you are assigning value of data variable to your test directive's $scope.value
.
Directive is compiling only once so once it compiles it will bind the value after than any change in parent scope's variable is not affecting here because you created isolated scope and copied value from parent's scope.
For better understanding of directive check out this article
Upvotes: 1