Skip to content

Commit 6af5d68

Browse files
committed
feat(collectionRepeat): huge optimization upgrades
Closes #1597
1 parent 8c36a66 commit 6af5d68

File tree

6 files changed

+357
-295
lines changed

6 files changed

+357
-295
lines changed

js/angular/directive/collectionRepeat.js

+2-8
Original file line numberDiff line numberDiff line change
@@ -158,15 +158,9 @@ function($collectionRepeatManager, $collectionDataSource, $parse) {
158158
} else if (!isVertical && !$attr.collectionItemWidth) {
159159
throw new Error(COLLECTION_REPEAT_ATTR_WIDTH_ERROR);
160160
}
161-
$attr.collectionItemHeight = $attr.collectionItemHeight || '"100%"';
162-
$attr.collectionItemWidth = $attr.collectionItemWidth || '"100%"';
163161

164-
var heightParsed = $attr.collectionItemHeight ?
165-
$parse($attr.collectionItemHeight) :
166-
function() { return scrollView.__clientHeight; };
167-
var widthParsed = $attr.collectionItemWidth ?
168-
$parse($attr.collectionItemWidth) :
169-
function() { return scrollView.__clientWidth; };
162+
var heightParsed = $parse($attr.collectionItemHeight || '"100%"');
163+
var widthParsed = $parse($attr.collectionItemWidth || '"100%"');
170164

171165
var heightGetter = function(scope, locals) {
172166
var result = heightParsed(scope, locals);

js/angular/service/collectionRepeatDataSource.js

+64-48
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ IonicModule
44
'$parse',
55
'$rootScope',
66
function($cacheFactory, $parse, $rootScope) {
7-
var nextCacheId = 0;
7+
88
function CollectionRepeatDataSource(options) {
99
var self = this;
1010
this.scope = options.scope;
@@ -35,33 +35,28 @@ function($cacheFactory, $parse, $rootScope) {
3535
};
3636
}
3737

38-
var cacheKeys = {};
39-
this.itemCache = $cacheFactory(nextCacheId++, {size: 500});
40-
41-
var _put = this.itemCache.put;
42-
this.itemCache.put = function(key, value) {
43-
cacheKeys[key] = true;
44-
return _put(key, value);
45-
};
46-
47-
var _remove = this.itemCache.remove;
48-
this.itemCache.remove = function(key) {
49-
delete cacheKeys[key];
50-
return _remove(key);
51-
};
52-
this.itemCache.keys = function() {
53-
return Object.keys(cacheKeys);
54-
};
38+
this.attachedItems = {};
39+
this.BACKUP_ITEMS_LENGTH = 10;
40+
this.backupItemsArray = [];
5541
}
5642
CollectionRepeatDataSource.prototype = {
43+
setup: function() {
44+
for (var i = 0; i < this.BACKUP_ITEMS_LENGTH; i++) {
45+
this.detachItem(this.createItem());
46+
}
47+
},
5748
destroy: function() {
5849
this.dimensions.length = 0;
59-
this.itemCache.keys().forEach(function(key) {
60-
var item = this.itemCache.get(key);
61-
item.element.remove();
62-
item.scope.$destroy();
50+
this.data = null;
51+
forEach(this.backupItemsArray, function(item) {
52+
this.destroyItem(item);
53+
}, this);
54+
this.backupItemsArray.length = 0;
55+
56+
forEach(this.attachedItems, function(item, key) {
57+
this.destroyItem(item);
6358
}, this);
64-
this.itemCache.removeAll();
59+
this.attachedItems = {};
6560
},
6661
calculateDataDimensions: function() {
6762
var locals = {};
@@ -74,53 +69,74 @@ function($cacheFactory, $parse, $rootScope) {
7469
};
7570
}, this);
7671
},
77-
compileItem: function(index, value) {
78-
var key = this.itemHashGetter(index, value);
79-
var cachedItem = this.itemCache.get(key);
80-
if (cachedItem) return cachedItem;
81-
72+
createItem: function() {
8273
var item = {};
8374
item.scope = this.scope.$new();
84-
item.scope[this.keyExpr] = value;
8575

8676
this.transcludeFn(item.scope, function(clone) {
8777
clone.css('position', 'absolute');
8878
item.element = clone;
8979
});
9080

91-
return this.itemCache.put(key, item);
81+
this.transcludeParent.append(item.element);
82+
83+
return item;
9284
},
93-
getItem: function(index) {
85+
getItem: function(hash) {
86+
window.AMOUNT = window.AMOUNT || 0;
87+
if ( (item = this.attachedItems[hash]) ) {
88+
//do nothing, the item is good
89+
} else if ( (item = this.backupItemsArray.pop()) ) {
90+
reconnectScope(item.scope);
91+
} else {
92+
AMOUNT++;
93+
item = this.createItem();
94+
}
95+
return item;
96+
},
97+
attachItemAtIndex: function(index) {
9498
var value = this.data[index];
95-
var item = this.compileItem(index, value);
99+
var hash = this.itemHashGetter(index, value);
100+
var item = this.getItem(hash);
96101

97-
if (item.scope.$index !== index) {
102+
if (item.scope.$index !== index || item.scope[this.keyExpr] !== value) {
103+
item.scope[this.keyExpr] = value;
98104
item.scope.$index = index;
99105
item.scope.$first = (index === 0);
100106
item.scope.$last = (index === (this.getLength() - 1));
101107
item.scope.$middle = !(item.scope.$first || item.scope.$last);
102108
item.scope.$odd = !(item.scope.$even = (index&1) === 0);
109+
110+
//We changed the scope, so digest if needed
111+
if (!$rootScope.$$phase) {
112+
item.scope.$digest();
113+
}
103114
}
104115

116+
item.hash = hash;
117+
this.attachedItems[hash] = item;
118+
105119
return item;
106120
},
107-
detachItem: function(item) {
108-
var i, node, parent;
109-
//Don't .remove(), that will destroy element data
110-
for (i = 0; i < item.element.length; i++) {
111-
node = item.element[i];
112-
parent = node.parentNode;
113-
parent && parent.removeChild(node);
114-
}
115-
//Don't .$destroy(), just stop watchers and events firing
116-
disconnectScope(item.scope);
121+
destroyItem: function(item) {
122+
item.element.remove();
123+
item.scope.$destroy();
124+
item.scope = null;
125+
item.element = null;
117126
},
118-
attachItem: function(item) {
119-
if (!item.element[0].parentNode) {
120-
this.transcludeParent[0].appendChild(item.element[0]);
127+
detachItem: function(item) {
128+
delete this.attachedItems[item.hash];
129+
130+
// If we are at the limit of backup items, just get rid of the this element
131+
if (this.backupItemsArray.length >= this.BACKUP_ITEMS_LENGTH) {
132+
this.destroyItem(item);
133+
// Otherwise, add it to our backup items
134+
} else {
135+
this.backupItemsArray.push(item);
136+
item.element.css(ionic.CSS.TRANSFORM, 'translate3d(-2000px,-2000px,0)');
137+
//Don't .$destroy(), just stop watchers and events firing
138+
disconnectScope(item.scope);
121139
}
122-
reconnectScope(item.scope);
123-
!$rootScope.$$phase && item.scope.$digest();
124140
},
125141
getLength: function() {
126142
return this.data && this.data.length || 0;

0 commit comments

Comments
 (0)