mirror of
https://github.com/Polymer/polymer.git
synced 2025-02-25 18:55:30 -06:00
Adds restamp mode to dom-repeat.
This commit is contained in:
@@ -262,6 +262,32 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
_targetFrameTime: {
|
||||
type: Number,
|
||||
computed: '__computeFrameTime(targetFramerate)'
|
||||
},
|
||||
|
||||
/**
|
||||
* When `restamp` is true, template instances are never reused with
|
||||
* different items. Instances mapping to current items are moved
|
||||
* to their new location, new instances are created for any new
|
||||
* items, and instances for removed items are discarded. This mode is
|
||||
* generally more expensive than `restamp: false` as it results in
|
||||
* more instances to be created and discarded during updates and
|
||||
* disconnection/reconnection churn. By default, object identity is
|
||||
* used for mapping instances to items. Set `restampKey` to provide
|
||||
* key based identity.
|
||||
*/
|
||||
restamp: {
|
||||
type: Boolean
|
||||
},
|
||||
|
||||
/**
|
||||
* When `restamp: true` is used, `restampKey` can be used to provide
|
||||
* a path into the item object that serves as a unique key for the
|
||||
* item, for purposes of mapping instances to items. Instances for
|
||||
* items with the same key will be reused, while all others will be
|
||||
* either restamped or discarded.
|
||||
*/
|
||||
restampKey: {
|
||||
type: String
|
||||
}
|
||||
|
||||
}
|
||||
@@ -276,7 +302,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
super();
|
||||
this.__instances = [];
|
||||
this.__limit = Infinity;
|
||||
this.__pool = [];
|
||||
this.__renderDebouncer = null;
|
||||
this.__itemsIdxToInstIdx = {};
|
||||
this.__chunkCount = null;
|
||||
@@ -291,8 +316,9 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this.__isDetached = true;
|
||||
for (let i=0; i<this.__instances.length; i++) {
|
||||
this.__detachInstance(i);
|
||||
let instances = this.__instances;
|
||||
for (let i=0; i<instances.length; i++) {
|
||||
this.__detachInstance(instances[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,9 +327,9 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
// only perform attachment if the element was previously detached.
|
||||
if (this.__isDetached) {
|
||||
this.__isDetached = false;
|
||||
let parent = this.parentNode;
|
||||
for (let i=0; i<this.__instances.length; i++) {
|
||||
this.__attachInstance(i, parent);
|
||||
let instances = this.__instances;
|
||||
for (let i=0; i<instances.length; i++) {
|
||||
this.__attachInstance(instances[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -487,13 +513,34 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
// No template found yet
|
||||
return;
|
||||
}
|
||||
this.__applyFullRefresh();
|
||||
// Reset the pool
|
||||
// TODO(kschaaf): Reuse pool across turns and nested templates
|
||||
// Now that objects/arrays are re-evaluated when set, we can safely
|
||||
// reuse pooled instances across turns, however we still need to decide
|
||||
// semantics regarding how long to hold, how many to hold, etc.
|
||||
this.__pool.length = 0;
|
||||
const items = this.items || [];
|
||||
let inst2items = new Array(items.length);
|
||||
for (let i=0; i<items.length; i++) {
|
||||
inst2items[i] = i;
|
||||
}
|
||||
// Apply user filter
|
||||
if (this.__filterFn) {
|
||||
inst2items = inst2items.filter((i, idx) =>
|
||||
this.__filterFn(items[i], idx, items));
|
||||
}
|
||||
// Apply user sort
|
||||
if (this.__sortFn) {
|
||||
inst2items.sort((a, b) => this.__sortFn(items[a], items[b]));
|
||||
}
|
||||
// items->inst map kept for item path forwarding
|
||||
const items2inst = this.__itemsIdxToInstIdx = {};
|
||||
const instances = this.__instances;
|
||||
const limit = Math.max(Math.min(inst2items.length, this.__limit), 0);
|
||||
// Generate instances and assign items
|
||||
if (this.restamp) {
|
||||
this.__renderRestamp(items, instances, limit, inst2items, items2inst);
|
||||
} else {
|
||||
this.__renderRefresh(items, instances, limit, inst2items, items2inst);
|
||||
}
|
||||
// Remove any extra instances from previous state
|
||||
while (this.__instances.length > limit) {
|
||||
this.__detachAndRemoveInstanceAt(limit);
|
||||
}
|
||||
// Set rendered item count
|
||||
this._setRenderedItemCount(this.__instances.length);
|
||||
// Notify users
|
||||
@@ -505,65 +552,90 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
this.__tryRenderChunk();
|
||||
}
|
||||
|
||||
__applyFullRefresh() {
|
||||
const items = this.items || [];
|
||||
let isntIdxToItemsIdx = new Array(items.length);
|
||||
for (let i=0; i<items.length; i++) {
|
||||
isntIdxToItemsIdx[i] = i;
|
||||
}
|
||||
// Apply user filter
|
||||
if (this.__filterFn) {
|
||||
isntIdxToItemsIdx = isntIdxToItemsIdx.filter((i, idx, array) =>
|
||||
this.__filterFn(items[i], idx, array));
|
||||
}
|
||||
// Apply user sort
|
||||
if (this.__sortFn) {
|
||||
isntIdxToItemsIdx.sort((a, b) => this.__sortFn(items[a], items[b]));
|
||||
}
|
||||
// items->inst map kept for item path forwarding
|
||||
const itemsIdxToInstIdx = this.__itemsIdxToInstIdx = {};
|
||||
let instIdx = 0;
|
||||
// Generate instances and assign items
|
||||
const limit = Math.min(isntIdxToItemsIdx.length, this.__limit);
|
||||
for (; instIdx<limit; instIdx++) {
|
||||
let inst = this.__instances[instIdx];
|
||||
let itemIdx = isntIdxToItemsIdx[instIdx];
|
||||
__renderRestamp(items, instances, limit, inst2items, items2inst) {
|
||||
let keyPath = this.restampKey;
|
||||
const instCache = new Map();
|
||||
nextItem: for (let i=0; i<limit; i++) {
|
||||
let itemIdx = inst2items[i];
|
||||
let item = items[itemIdx];
|
||||
itemsIdxToInstIdx[itemIdx] = instIdx;
|
||||
if (inst && instIdx < this.__limit) {
|
||||
let key = keyPath && Polymer.Path.get(item, keyPath) || item;
|
||||
let inst, instItem;
|
||||
while ((inst = instances[i])) {
|
||||
instItem = inst[this.as];
|
||||
let instKey = keyPath && Polymer.Path.get(instItem, keyPath) || instItem;
|
||||
if (key === instKey) {
|
||||
inst._setPendingProperty(this.as, item);
|
||||
inst._setPendingProperty(this.indexAs, i);
|
||||
inst._setPendingProperty(this.itemsIndexAs, itemIdx);
|
||||
inst._flushProperties();
|
||||
items2inst[itemIdx] = i;
|
||||
continue nextItem;
|
||||
}
|
||||
let cache = instCache.get(instKey);
|
||||
if (cache) {
|
||||
cache.push(inst);
|
||||
} else {
|
||||
instCache.set(instKey, [inst]);
|
||||
}
|
||||
instances.splice(i, 1);
|
||||
}
|
||||
let cache = instCache.get(key);
|
||||
if (cache && (inst = cache.shift())) {
|
||||
this.__reuseInstance(inst, item, i, itemIdx);
|
||||
if (!cache.length) {
|
||||
instCache.delete(key);
|
||||
}
|
||||
} else {
|
||||
this.__insertInstance(item, i, itemIdx);
|
||||
}
|
||||
items2inst[itemIdx] = i;
|
||||
}
|
||||
instCache.forEach(cache => cache.forEach(inst => this.__detachInstance(inst)));
|
||||
}
|
||||
|
||||
__renderRefresh(items, instances, limit, inst2items, items2inst) {
|
||||
for (let i=0; i<limit; i++) {
|
||||
let inst = instances[i];
|
||||
let itemIdx = inst2items[i];
|
||||
let item = items[itemIdx];
|
||||
if (inst) {
|
||||
inst._setPendingProperty(this.as, item);
|
||||
inst._setPendingProperty(this.indexAs, instIdx);
|
||||
inst._setPendingProperty(this.indexAs, i);
|
||||
inst._setPendingProperty(this.itemsIndexAs, itemIdx);
|
||||
inst._flushProperties();
|
||||
} else {
|
||||
this.__insertInstance(item, instIdx, itemIdx);
|
||||
this.__insertInstance(item, i, itemIdx);
|
||||
}
|
||||
}
|
||||
// Remove any extra instances from previous state
|
||||
for (let i=this.__instances.length-1; i>=instIdx; i--) {
|
||||
this.__detachAndRemoveInstance(i);
|
||||
items2inst[itemIdx] = i;
|
||||
}
|
||||
}
|
||||
|
||||
__detachInstance(idx) {
|
||||
let inst = this.__instances[idx];
|
||||
__detachInstance(inst) {
|
||||
for (let i=0; i<inst.children.length; i++) {
|
||||
let el = inst.children[i];
|
||||
inst.root.appendChild(el);
|
||||
}
|
||||
return inst;
|
||||
}
|
||||
|
||||
__attachInstance(idx, parent) {
|
||||
let inst = this.__instances[idx];
|
||||
parent.insertBefore(inst.root, this);
|
||||
}
|
||||
|
||||
__detachAndRemoveInstance(idx) {
|
||||
let inst = this.__detachInstance(idx);
|
||||
if (inst) {
|
||||
this.__pool.push(inst);
|
||||
__reuseInstance(inst, item, idx, itemIdx) {
|
||||
let beforeInst = this.__instances[idx];
|
||||
let beforeNode = beforeInst ? beforeInst.children[0] : this;
|
||||
for (let i=0; i<inst.children.length; i++) {
|
||||
this.parentNode.insertBefore(inst.children[i], beforeNode);
|
||||
}
|
||||
this.__instances.splice(idx, 0, inst);
|
||||
inst._setPendingProperty(this.as, item);
|
||||
inst._setPendingProperty(this.indexAs, idx);
|
||||
inst._setPendingProperty(this.itemsIndexAs, itemIdx);
|
||||
inst._flushProperties();
|
||||
}
|
||||
|
||||
__attachInstance(inst) {
|
||||
this.parentNode.insertBefore(inst.root, this);
|
||||
}
|
||||
|
||||
__detachAndRemoveInstanceAt(idx) {
|
||||
this.__detachInstance(this.__instances[idx]);
|
||||
this.__instances.splice(idx, 1);
|
||||
}
|
||||
|
||||
@@ -576,21 +648,11 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
}
|
||||
|
||||
__insertInstance(item, instIdx, itemIdx) {
|
||||
let inst = this.__pool.pop();
|
||||
if (inst) {
|
||||
// TODO(kschaaf): If the pool is shared across turns, hostProps
|
||||
// need to be re-set to reused instances in addition to item
|
||||
inst._setPendingProperty(this.as, item);
|
||||
inst._setPendingProperty(this.indexAs, instIdx);
|
||||
inst._setPendingProperty(this.itemsIndexAs, itemIdx);
|
||||
inst._flushProperties();
|
||||
} else {
|
||||
inst = this.__stampInstance(item, instIdx, itemIdx);
|
||||
}
|
||||
let beforeRow = this.__instances[instIdx + 1];
|
||||
let inst = this.__stampInstance(item, instIdx, itemIdx);
|
||||
let beforeRow = this.__instances[instIdx];
|
||||
let beforeNode = beforeRow ? beforeRow.children[0] : this;
|
||||
this.parentNode.insertBefore(inst.root, beforeNode);
|
||||
this.__instances[instIdx] = inst;
|
||||
this.__instances.splice(instIdx, 0, inst);
|
||||
return inst;
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
'unit/path.html',
|
||||
'unit/templatize.html',
|
||||
'unit/dom-repeat.html',
|
||||
'unit/dom-repeat.html?restamp',
|
||||
'unit/dom-if.html',
|
||||
'unit/dom-bind.html',
|
||||
'unit/array-selector.html',
|
||||
|
||||
@@ -15,6 +15,11 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
<script src="../../../web-component-tester/browser.js"></script>
|
||||
<link rel="import" href="../../polymer.html">
|
||||
<link rel="import" href="dom-repeat-elements.html">
|
||||
<script>
|
||||
if (document.location.search.indexOf('restamp') >= 0) {
|
||||
customElements.get('dom-repeat').prototype.restamp = true;
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
/* makes painting faster when testing on IOS simulator */
|
||||
x-repeat-chunked {
|
||||
@@ -301,6 +306,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
// reset sort fn
|
||||
configured.$.repeater.sort = null;
|
||||
configured.$.repeater.render();
|
||||
stamped = configured.root.querySelectorAll('*:not(template):not(dom-repeat)');
|
||||
assert.equal(stamped.length, 3 + 3*3 + 3*3*3, 'total stamped count incorrect');
|
||||
assert.equal(stamped[0].itemaProp, 'prop-1');
|
||||
assert.equal(stamped[0].indexa, 0);
|
||||
@@ -327,6 +333,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
// reset sort fn
|
||||
configured.$.repeater.sort = null;
|
||||
configured.$.repeater.render();
|
||||
stamped = configured.root.querySelectorAll('*:not(template):not(dom-repeat)');
|
||||
assert.equal(stamped.length, 3 + 3*3 + 3*3*3, 'total stamped count incorrect');
|
||||
assert.equal(stamped[0].itemaProp, 'prop-1');
|
||||
assert.equal(stamped[0].indexa, 0);
|
||||
@@ -3350,10 +3357,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
var prev = items.slice();
|
||||
items.sort();
|
||||
var splices = Polymer.ArraySplice.calculateSplices(items, prev);
|
||||
var change = {
|
||||
indexSplices: splices
|
||||
};
|
||||
primitive.set('items.splices', change);
|
||||
primitive.notifySplices('items', splices);
|
||||
setTimeout(function() {
|
||||
var stamped1 = primitive.$.container1.querySelectorAll('*:not(template):not(dom-repeat)');
|
||||
for (var i=0; i<items.length; i++) {
|
||||
|
||||
Reference in New Issue
Block a user