Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(collectionRepeat): huge optimization upgrades #1597

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 2 additions & 8 deletions js/angular/directive/collectionRepeat.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,15 +158,9 @@ function($collectionRepeatManager, $collectionDataSource, $parse) {
} else if (!isVertical && !$attr.collectionItemWidth) {
throw new Error(COLLECTION_REPEAT_ATTR_WIDTH_ERROR);
}
$attr.collectionItemHeight = $attr.collectionItemHeight || '"100%"';
$attr.collectionItemWidth = $attr.collectionItemWidth || '"100%"';

var heightParsed = $attr.collectionItemHeight ?
$parse($attr.collectionItemHeight) :
function() { return scrollView.__clientHeight; };
var widthParsed = $attr.collectionItemWidth ?
$parse($attr.collectionItemWidth) :
function() { return scrollView.__clientWidth; };
var heightParsed = $parse($attr.collectionItemHeight || '"100%"');
var widthParsed = $parse($attr.collectionItemWidth || '"100%"');

var heightGetter = function(scope, locals) {
var result = heightParsed(scope, locals);
Expand Down
112 changes: 64 additions & 48 deletions js/angular/service/collectionRepeatDataSource.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ IonicModule
'$parse',
'$rootScope',
function($cacheFactory, $parse, $rootScope) {
var nextCacheId = 0;

function CollectionRepeatDataSource(options) {
var self = this;
this.scope = options.scope;
Expand Down Expand Up @@ -35,33 +35,28 @@ function($cacheFactory, $parse, $rootScope) {
};
}

var cacheKeys = {};
this.itemCache = $cacheFactory(nextCacheId++, {size: 500});

var _put = this.itemCache.put;
this.itemCache.put = function(key, value) {
cacheKeys[key] = true;
return _put(key, value);
};

var _remove = this.itemCache.remove;
this.itemCache.remove = function(key) {
delete cacheKeys[key];
return _remove(key);
};
this.itemCache.keys = function() {
return Object.keys(cacheKeys);
};
this.attachedItems = {};
this.BACKUP_ITEMS_LENGTH = 10;
this.backupItemsArray = [];
}
CollectionRepeatDataSource.prototype = {
setup: function() {
for (var i = 0; i < this.BACKUP_ITEMS_LENGTH; i++) {
this.detachItem(this.createItem());
}
},
destroy: function() {
this.dimensions.length = 0;
this.itemCache.keys().forEach(function(key) {
var item = this.itemCache.get(key);
item.element.remove();
item.scope.$destroy();
this.data = null;
forEach(this.backupItemsArray, function(item) {
this.destroyItem(item);
}, this);
this.backupItemsArray.length = 0;

forEach(this.attachedItems, function(item, key) {
this.destroyItem(item);
}, this);
this.itemCache.removeAll();
this.attachedItems = {};
},
calculateDataDimensions: function() {
var locals = {};
Expand All @@ -74,53 +69,74 @@ function($cacheFactory, $parse, $rootScope) {
};
}, this);
},
compileItem: function(index, value) {
var key = this.itemHashGetter(index, value);
var cachedItem = this.itemCache.get(key);
if (cachedItem) return cachedItem;

createItem: function() {
var item = {};
item.scope = this.scope.$new();
item.scope[this.keyExpr] = value;

this.transcludeFn(item.scope, function(clone) {
clone.css('position', 'absolute');
item.element = clone;
});

return this.itemCache.put(key, item);
this.transcludeParent.append(item.element);

return item;
},
getItem: function(index) {
getItem: function(hash) {
window.AMOUNT = window.AMOUNT || 0;
if ( (item = this.attachedItems[hash]) ) {
//do nothing, the item is good
} else if ( (item = this.backupItemsArray.pop()) ) {
reconnectScope(item.scope);
} else {
AMOUNT++;
item = this.createItem();
}
return item;
},
attachItemAtIndex: function(index) {
var value = this.data[index];
var item = this.compileItem(index, value);
var hash = this.itemHashGetter(index, value);
var item = this.getItem(hash);

if (item.scope.$index !== index) {
if (item.scope.$index !== index || item.scope[this.keyExpr] !== value) {
item.scope[this.keyExpr] = value;
item.scope.$index = index;
item.scope.$first = (index === 0);
item.scope.$last = (index === (this.getLength() - 1));
item.scope.$middle = !(item.scope.$first || item.scope.$last);
item.scope.$odd = !(item.scope.$even = (index&1) === 0);

//We changed the scope, so digest if needed
if (!$rootScope.$$phase) {
item.scope.$digest();
}
}

item.hash = hash;
this.attachedItems[hash] = item;

return item;
},
detachItem: function(item) {
var i, node, parent;
//Don't .remove(), that will destroy element data
for (i = 0; i < item.element.length; i++) {
node = item.element[i];
parent = node.parentNode;
parent && parent.removeChild(node);
}
//Don't .$destroy(), just stop watchers and events firing
disconnectScope(item.scope);
destroyItem: function(item) {
item.element.remove();
item.scope.$destroy();
item.scope = null;
item.element = null;
},
attachItem: function(item) {
if (!item.element[0].parentNode) {
this.transcludeParent[0].appendChild(item.element[0]);
detachItem: function(item) {
delete this.attachedItems[item.hash];

// If we are at the limit of backup items, just get rid of the this element
if (this.backupItemsArray.length >= this.BACKUP_ITEMS_LENGTH) {
this.destroyItem(item);
// Otherwise, add it to our backup items
} else {
this.backupItemsArray.push(item);
item.element.css(ionic.CSS.TRANSFORM, 'translate3d(-2000px,-2000px,0)');
//Don't .$destroy(), just stop watchers and events firing
disconnectScope(item.scope);
}
reconnectScope(item.scope);
!$rootScope.$$phase && item.scope.$digest();
},
getLength: function() {
return this.data && this.data.length || 0;
Expand Down
Loading