Reputation: 474
I am trying to create recursive grid layout using directives.
My problem is when I delete a grid using remove button inside it- it gets removed from appliedgrid container but two way data binding doesn't work as it should. In place of current grid, last grid gets removed from the UI.
Link- http://plnkr.co/edit/DzKIHKvJdLoZiYY3jgDx?p=preview
Steps to reproduce: 1) Click remove button on first grid, you will see that in place of first, second grid gets removed. While json data of appliedgrid contains second grid inside it. So two way binding of angular doesn't work as it supposed to.
Upvotes: 2
Views: 632
Reputation: 4303
I did a little thinking in my previous answer and it turns out it was not correct.
Firstly, do not use track by $index
. It makes no sense in your case. track by
is an optimisation of ng-repeat
to correlate (potentially new) objects in the array that are "business-wise" equal with old objects in the array, so that it re-uses scopes and DOM elements in an effort to minimize DOM manipulation. That is: if you give ng-repeat
a hint which new object in the new array is "equal" to an old object in the old array, it will reuse its scope and hopping that the new object is not dramatically different compared to the old one, less $watch
callbacks will fire and less DOM updates will occur.
Your actual problem is that you are "statically" or "once-off" binding data with statements like:
$scope.gridIndex = $parse($attrs.gridIndex)($scope);
$scope.gridValues=$parse($attrs.appliedgrid)($scope);
$scope.gridParent=$parse($attrs.appliedgrids)($scope);
The first grid
item is indeed removed from the array but ng-repeat
does not remove its scope and DOM element because track by $index
is used. But still, the new 0-index object (2nd, previously) is used to update the scope (the one created for the 1st object).
You do not see this reflecting to the UI because $scope.gridValues
was evaluated in the beginning and is not evaluated again.
So, even though $scope.appliedgrid
now points to [{span:12,data:[object]}]
, $scope.gridValues
still points to [{span:6,data:[object]},{span:6,data:[grid2]}]
.
Removing track by $index
solves the problem because ng-repeat
tracks objects by reference so each object is associated with the same scope until it is removed from the array.
You can verify it with AngScope, a small Firebug-based scope inspector. You have to open it in a separate tab with "Launch the preview in a separate window" in order for it to work in plunker.
I tried to find a quick fix for it but there was no luck. I guess, you have to re-write it using isolated scopes and real 2-way binding.
Upvotes: 2
Reputation: 4303
Short answer: remove track by $index
from ng-repeat
.
Long answer: When you are write track by $index
you're actually saying to ng-repeat
that:
When you remove the 1st object from the array, angular digests and finds out the following:
This is because when ng-repeat
runs again, your previously 2nd object which was tagged as "1", is now your 1st and only object which is tagged as "0", since the $index
is evaluated again starting from 0.
Angular believes that the 1st DOM element still points to the same object cause it finds it tagged as "0", regardless that it's a completly different object. Under the hood, $scope
has the correct model values but ng-repeat
skips re-rendering of the DOM element.
It very difficult to write down what really happens. Hope I got it right and helped you.
Upvotes: 1