Open jasonkuhrt opened 11 years ago
Hi there! (I'm one of the guys from the Google Groups discussion...)
As I see it, the problem is that the ng-repeat directive (https://github.com/angular/angular.js/blob/master/src/ng/directive/ngRepeat.js) tracks changes to the given list in a quite complex way, assigning hash functions to the elements of the array, and not the way angular.equals (http://docs.angularjs.org/api/angular.equals) does it. As far as I can tell, angular.equals does a "deep compare" on arrays, and recursively so for arrays of arrays, etc.
When writing a chunk filter (my attempt: https://github.com/olefriis/simplepvr-backend-ruby/blob/master/public/js/filters.js) the most obvious way to do it is to... well, chunk the input array into a number of new arrays. This means that each time such a chunk filter is invoked, it will create new array instances, and ng-repeat will treat the output as completely new values each time.
If we could just make ng-repeat use angular.equals, all would be fine. I am aware that this may be slowing down execution for insanely large data sets, but having some way to switch on this behavior would be nice.
That's my 50 cent. Hope it's useful, and thanks for reading!
As part of our effort to clean out old issues, this issue is being automatically closed since it has been inactivite for over two months.
Please try the newest versions of Angular (1.0.8
and 1.2.0-rc.1
), and if the issue persists, comment below so we can discuss it.
Thanks!
@btford the issue is outlined clearly above. I would appreciate at least a single comment from the Angular team rather than asking us to try a new version of angular and add yet more feedback.
Thanks
Hello, I ran in the same issue to. the $$hashkey value is missing on newly created elements. the problem is well described in the google group post.
Thank you
See track by
in AngularJS 1.2.0. It should solve this issue.
I tried using track by
, but could not make it solve my problem. I wanted to create Twitter Bootstrap "row" divs with exactly 3 sub-divs in each, which is why I wrote my own chunk
filter (which chunks one array into an array of arrays, each of a specified size). I cannot make this chunk
filter work with AngularJS 1.2, and I did try using track by
. I assume it's just me being too dumb, since the AngularJS developers are generally really awesome and very intelligent people.
However, in AngularJS 1.2 there's the ng-repeat-start
and ng-repeat-end
directives, and using these, I can periodically insert a "clearfix" div, which is good enough for me. See here for an example.
I might try a second time to make my chunk
filter work in the weekend. If anybody has a jsfiddle or something showing how to do a chunk
filter in AngularJS 1.2, I'll be very interested.
So, I've fiddled a bit with track by
and my own chunk
filter. I have made a very simple example which shows that I'm too stupid to make it work :-) - github.com/olefriis/angularjs-chunk
I've read the docs on track by
, and I think I get it, but I still cannot make it work.
...and here's a JSFiddle in case you prefer that. http://jsfiddle.net/olefriis/cag9a/1/
When running the fiddle, the output is correct, but Angular throws a digest error in the JavaScript console.
I'm not sure that track by
helps us with the problem because I think you have to use it before the filter. If we could do something like ng-repeat item in items | chunk:3 track by $index
maybe it would work. (I might be wrong there!) My attempt http://plnkr.co/edit/Nyfa9bSOeBYBTfKmJ0oY?p=preview
Edit:
I have managed to get a hack working but I'm not sure how performant it'll be! (I serialise each chunk to JSON and then parse as I use it.) Here you go anyway http://plnkr.co/edit/XT3tJMlBKvIodYUkoVkO?p=preview
but if you will filter $scope.$watch array like in your exercise, you will have: Uncaught Error: 10 $digest() iterations reached. Aborting!
i use this pattern:
<div ng-repeat="item in array" ng-switch on="$index % 2">
<div class="row" ng-switch-when="0">
<div class="col-md-6" render-item-directive directive-var="array[$index+0]"></div>
<div class="col-md-6" render-item-directive directive-var="array[$index+1]"></div>
</div>
</div>
That's a nice idea. However, in case you have an odd number of elements in your array, the last "array[$index+1]" will be "undefined", which you then need to handle in your directive.
which way is better to handle it?
+1
Started using Angular today, this was the first issue I came across, wrote chuck function similar to above but angular seemed to be calling the function far more times than expected, and thus through errors. Having searched the web I'm surprised a solution has not been made available for what seems such a simple problem which could have be resolved in minutes using knockout.
if you want return new collection, you just need to save javascript links. I wrote factory for this case:
app.factory('linker', function () {
var links = {}
return function (arr, key) {
var link = links[key] || []
arr.forEach(function (newItem, index) {
var oldItem = link[index]
if (!angular.equals(oldItem, newItem))
link[index] = newItem
})
link.length = arr.length
return links[key] = link
}
})
you can see here how it works: http://plnkr.co/edit/2Uc5zsFgVnK3ltHOUUQx?p=preview
I have a solution with a high-priority directive - is this clean? Feels better than some of the solutions posted so far:
Was there ever any resolution to this issue? I just ran into this on angular 1.4.7...
The "resolution" is quite simple: to display your items (length = n) in group of 3, just make your ng-repeat on 0..n/3 and display items[i * 3], items[i * 3 + 1] and items [i * 3 + 2] in each group
I would call that a workaround, not a resolution.
To me, a resolution would:
ng-repeat
on objects and not just arrays$index
, $first
, $last
, etc)For example:
<p ng-repeat="chunk in list | chunked:chunkSize">
<span ng-repeat="x in chunk">{{x}}<span ng-if="!$last">,</span> </span>
</p>
$scope.chunkSize = 3;
$scope.list = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
Angular works by dirty checking, so if the scope shows to the dirty checking code a new object at each loop, it just can't work. I would not call that a bug needing a resolution but just the way you must use angular.
IMO this probably shouldn't be supported in the framework itself. There are multiple ways around this now, and an API in core that a lot of people won't use is the wrong approach.
One can for example do
<div ng-repeat-start="item in items track by item.id">
</div>
<div ng-repeat-end
ng-if="($index + 1) % 3">
Display
</div>
Plunker of this in action here.
The benefit of this is that it allows more flexibility. If performance is an issue, then one should be already building an abstraction above/without ng-repeat anyway.
There is a thorough discussion including analysis, ideas, and fiddles here (concluding with some successful work-arounds):
https://groups.google.com/forum/#!msg/angular/IEIQok-YkpU/oKuLvzCnAcoJ
A common instigating use-case is chunking arrays for grids:
http://stackoverflow.com/questions/11056819/how-would-i-use-angularjs-ng-repeat-with-twitter-bootstraps-scaffolding
I believe the issue can be summarized as difficulty using ng-repeat with filters that change the collection's reference...? Brainstorming possible directions forward:
track by
https://github.com/angular/angular.js/commit/61f2767ce65562257599649d9eaf9da08f321655 syntax make it possible to use filters that subsequently change the collection reference? It seems to metrack by
and this issue have nothing to do with each other ultimately.Could/should this issue by resolved by new additions to angular's destructing syntax? i.e.
Seems to me to be too specific a feature to make part of the destructing syntax.
An angular $service that supports creating filters that modifier collection reference within
ng-repeat
. So thatchunk
or any other filter could be confidently applied tong-repeat
:What users are rolling on their own: http://jsfiddle.net/pkozlowski_opensource/zvpVg/2/