diff --git a/lib/elements/dom-repeat.html b/lib/elements/dom-repeat.html index 0cbc10f219..17a1401588 100644 --- a/lib/elements/dom-repeat.html +++ b/lib/elements/dom-repeat.html @@ -265,32 +265,6 @@ _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 } } @@ -305,6 +279,7 @@ super(); this.__instances = []; this.__limit = Infinity; + this.__pool = []; this.__renderDebouncer = null; this.__itemsIdxToInstIdx = {}; this.__chunkCount = null; @@ -320,9 +295,8 @@ disconnectedCallback() { super.disconnectedCallback(); this.__isDetached = true; - let instances = this.__instances; - for (let i=0; i - this.__filterFn(items[i], idx, array)); - } - // 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); - } + 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; // Set rendered item count this._setRenderedItemCount(this.__instances.length); // Notify users @@ -563,90 +515,65 @@ this.__tryRenderChunk(); } - __renderRestamp(items, instances, limit, inst2items, items2inst) { - let keyPath = this.restampKey; - const instCache = new Map(); - nextItem: for (let i=0; i cache.forEach(inst => this.__detachInstance(inst))); - } - - __renderRefresh(items, instances, limit, inst2items, items2inst) { - for (let i=0; i + 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=instIdx; i--) { + this.__detachAndRemoveInstance(i); } } - __detachInstance(inst) { + __detachInstance(idx) { + let inst = this.__instances[idx]; for (let i=0; ix-repeat-chunked stamped[38] .. 3-3-3 */ - function setupFixture(fixtureName) { - var el = fixture(fixtureName); - if (document.location.search.indexOf('restamp') >= 0) { - el.restamp = true; - } - return el; - } - function arrayDelete(el, path, item) { var array = el.get(path); var idx = array.indexOf(item); @@ -178,7 +170,7 @@

x-repeat-chunked

let configured; setup(function() { - configured = setupFixture('configured'); + configured = fixture('configured'); Polymer.flush(); }); @@ -310,7 +302,6 @@

x-repeat-chunked

// 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); @@ -337,7 +328,6 @@

x-repeat-chunked

// 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); @@ -654,7 +644,7 @@

x-repeat-chunked

let unconfigured; setup(function() { - unconfigured = setupFixture('unconfigured'); + unconfigured = fixture('unconfigured'); unconfigured.items = window.getData(); unconfigured.$.el1.prop = 'outer'; unconfigured.$.el1.item = {prop: 'outerItem'}; @@ -787,7 +777,7 @@

x-repeat-chunked

let unconfigured; setup(function() { - unconfigured = setupFixture('unconfigured'); + unconfigured = fixture('unconfigured'); unconfigured.items = window.getData(); Polymer.flush(); }); @@ -875,7 +865,7 @@

x-repeat-chunked

let unconfigured; setup(function() { - unconfigured = setupFixture('unconfigured'); + unconfigured = fixture('unconfigured'); unconfigured.items = window.getData(); Polymer.flush(); unconfigured.$.el1.domUpdateHandlerCount = 0; @@ -1128,7 +1118,7 @@

x-repeat-chunked

let unconfigured; setup(function() { - unconfigured = setupFixture('unconfigured'); + unconfigured = fixture('unconfigured'); unconfigured.items = window.getData(); unconfigured.$.el1.$.repeater.filter = function(o) { return o.prop.indexOf('2') < 0; @@ -1338,7 +1328,7 @@

x-repeat-chunked

let unconfigured; setup(function() { - unconfigured = setupFixture('unconfigured'); + unconfigured = fixture('unconfigured'); unconfigured.items = window.getData(); unconfigured.$.el1.$.repeater.sort = function(a, b) { return b.prop == a.prop ? 0 : b.prop < a.prop ? -1 : 1; @@ -1547,7 +1537,7 @@

x-repeat-chunked

var unconfigured setup(function() { - unconfigured = setupFixture('unconfigured'); + unconfigured = fixture('unconfigured'); unconfigured.items = window.getData(); unconfigured.$.el1.$.repeater.sort = function(a, b) { return b.prop == a.prop ? 0 : b.prop < a.prop ? -1 : 1; @@ -1746,7 +1736,7 @@

x-repeat-chunked

let unconfigured; setup(function() { - unconfigured = setupFixture('unconfigured-mutable'); + unconfigured = fixture('unconfigured-mutable'); unconfigured.items = window.getData(); Polymer.flush(); }); @@ -2046,7 +2036,7 @@

x-repeat-chunked

let unconfigured; setup(function() { - unconfigured = setupFixture('unconfigured-mutable'); + unconfigured = fixture('unconfigured-mutable'); unconfigured.items = window.getData(); unconfigured.$.el1.$.repeater.filter = function(o) { return o.prop.indexOf('2') < 0; @@ -2263,7 +2253,7 @@

x-repeat-chunked

let unconfigured; setup(function() { - unconfigured = setupFixture('unconfigured-mutable'); + unconfigured = fixture('unconfigured-mutable'); unconfigured.items = window.getData(); unconfigured.$.el1.$.repeater.sort = function(a, b) { return b.prop == a.prop ? 0 : b.prop < a.prop ? -1 : 1; @@ -2479,7 +2469,7 @@

x-repeat-chunked

let unconfigured; setup(function() { - unconfigured = setupFixture('unconfigured-mutable'); + unconfigured = fixture('unconfigured-mutable'); unconfigured.items = window.getData(); unconfigured.$.el1.$.repeater.sort = function(a, b) { return b.prop == a.prop ? 0 : b.prop < a.prop ? -1 : 1; @@ -2685,7 +2675,7 @@

x-repeat-chunked

let primitive; setup(function() { - primitive = setupFixture('primitive'); + primitive = fixture('primitive'); Polymer.flush(); }); @@ -3231,7 +3221,7 @@

x-repeat-chunked

let primitive; setup(function() { - primitive = setupFixture('primitive'); + primitive = fixture('primitive'); Polymer.flush(); }); @@ -3288,7 +3278,7 @@

x-repeat-chunked

}); test('structured values - initial stamping', function() { - let unconfigured = setupFixture('unconfigured'); + let unconfigured = fixture('unconfigured'); unconfigured.items = window.getData(); Polymer.flush(); var stamped1 = unconfigured.$.el1.root.querySelectorAll('*:not(template):not(dom-repeat)'); @@ -3298,7 +3288,7 @@

x-repeat-chunked

}); test('structured values - change from inside', function() { - let unconfigured = setupFixture('unconfigured'); + let unconfigured = fixture('unconfigured'); unconfigured.items = window.getData(); Polymer.flush(); var stamped1 = unconfigured.$.el1.root.querySelectorAll('*:not(template):not(dom-repeat)'); @@ -3315,7 +3305,7 @@

x-repeat-chunked

}); test('structured values - sorted', function() { - let unconfigured = setupFixture('unconfigured'); + let unconfigured = fixture('unconfigured'); unconfigured.items = window.getData(); Polymer.flush(); unconfigured.$.el1.$.repeater.sort = function(a, b) { @@ -3351,7 +3341,7 @@

x-repeat-chunked

836, 564, 721, 540, 372, 244, 145, 525, 958, 595, 207, 103, 602, 769, 190]; - let primitive = setupFixture('primitive'); + let primitive = fixture('primitive'); primitive.items = items; setTimeout(function() { var stamped1 = primitive.$.container1.querySelectorAll('*:not(template):not(dom-repeat)'); @@ -3361,7 +3351,10 @@

x-repeat-chunked

var prev = items.slice(); items.sort(); var splices = Polymer.ArraySplice.calculateSplices(items, prev); - primitive.notifySplices('items', splices); + var change = { + indexSplices: splices + }; + primitive.set('items.splices', change); setTimeout(function() { var stamped1 = primitive.$.container1.querySelectorAll('*:not(template):not(dom-repeat)'); for (var i=0; ix-repeat-chunked let unconfigured; setup(function() { - unconfigured = setupFixture('unconfigured'); + unconfigured = fixture('unconfigured'); unconfigured.items = window.getData(); Polymer.flush(); }); @@ -3487,7 +3480,7 @@

x-repeat-chunked

}); test('renderedItemCount', function() { - let primitive = setupFixture('primitive'); + let primitive = fixture('primitive'); Polymer.flush(); var repeater1 = primitive.$.repeater1; primitive.items = [ 'a', 'b', 'c', 'd', 'e' ]; @@ -3507,7 +3500,7 @@

x-repeat-chunked

test('_showHideChildren', function() { // Initially all showing - let primitive = setupFixture('primitive'); + let primitive = fixture('primitive'); Polymer.flush(); var stamped1 = primitive.$.container1.querySelectorAll('*:not(template):not(dom-repeat)'); assert.equal(getComputedStyle(stamped1[0]).display, 'block'); @@ -4050,7 +4043,7 @@

x-repeat-chunked

suite('misc', function() { test('large splice', function(done) { - let primitiveLarge = setupFixture('primitiveLarge'); + let primitiveLarge = fixture('primitiveLarge'); Polymer.flush(); primitiveLarge.splice('items', 0, 10); setTimeout(function() { @@ -4066,7 +4059,7 @@

x-repeat-chunked

if (!window.ShadyDOM) { this.skip(); } - let simple = setupFixture('simple'); + let simple = fixture('simple'); Polymer.flush(); var removed; // Confirm initial scoping @@ -4097,7 +4090,7 @@

x-repeat-chunked

}); test('paths update on observed properties', function() { - let simple = setupFixture('simple'); + let simple = fixture('simple'); Polymer.flush(); //debugger; var stamped = simple.root.querySelectorAll('*:not(template):not(dom-repeat)'); @@ -4127,7 +4120,7 @@

x-repeat-chunked

suite('dom-change composed', function() { test('dom-change event composed, bubbles outside dom-if scope', function() { - var el = setupFixture('simple'); + var el = fixture('simple'); var domChangeFired = 0; el.addEventListener('dom-change', function() { domChangeFired++;