Skip to content

Commit

Permalink
Merge pull request #3160 from Polymer/3125-kschaaf-move-domif
Browse files Browse the repository at this point in the history
Ensure dom-if in host does not restamp when host detaches. Fixes #3125.
  • Loading branch information
Steve Orvell committed Dec 7, 2015
2 parents 2c6161c + 6467ae1 commit bab847a
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 19 deletions.
32 changes: 21 additions & 11 deletions src/lib/template/dom-if.html
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,14 @@
},

detached: function() {
// TODO(kschaaf): add logic to re-stamp in attached?
this._teardownInstance();
if (!this.parentNode) {
this._teardownInstance();
}
},

attached: function() {
if (this.if && this.ctor) {
// TODO(sorvell): should not be async, but node can be attached
// NOTE: ideally should not be async, but node can be attached
// when shady dom is in the act of distributing/composing so push it out
this.async(this._ensureInstance);
}
Expand Down Expand Up @@ -116,24 +117,33 @@
},

_ensureInstance: function() {
if (!this._instance) {
var parentNode = Polymer.dom(this).parentNode;
// Guard against element being detached while render was queued
if (parentNode) {
var parent = Polymer.dom(parentNode);
// TODO(sorvell): pickup stamping logic from x-repeat
var parentNode = Polymer.dom(this).parentNode;
// Guard against element being detached while render was queued
if (parentNode) {
var parent = Polymer.dom(parentNode);
if (!this._instance) {
this._instance = this.stamp();
var root = this._instance.root;
// TODO(sorvell): this incantation needs to be simpler.
parent.insertBefore(root, this);
} else {
var c$ = this._instance._children;
if (c$ && c$.length) {
// Detect case where dom-if was re-attached in new position
var lastChild = Polymer.dom(this).previousSibling;
if (lastChild !== c$[c$.length-1]) {
for (var i=0, n; (i<c$.length) && (n=c$[i]); i++) {
parent.insertBefore(n, this);
}
}
}
}
}
},

_teardownInstance: function() {
if (this._instance) {
var c$ = this._instance._children;
if (c$) {
if (c$ && c$.length) {
// use first child parent, for case when dom-if may have been detached
var parent = Polymer.dom(Polymer.dom(c$[0]).parentNode);
for (var i=0, n; (i<c$.length) && (n=c$[i]); i++) {
Expand Down
24 changes: 24 additions & 0 deletions test/unit/dom-if-elements.html
Original file line number Diff line number Diff line change
Expand Up @@ -180,3 +180,27 @@
});
</script>
</dom-module>

<dom-module id="x-host">
<template>
<template id="domif" is="dom-if" if>
<x-client></x-client>
<x-client></x-client>
<x-client></x-client>
</template>
</template>
<script>
Polymer({
is: 'x-host'
});
Polymer({
is: 'x-client',
statics: {
uid: 0
},
ready: function() {
this.uid = this.statics.uid++;
}
});
</script>
</dom-module>
136 changes: 128 additions & 8 deletions test/unit/dom-if.html
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,16 @@
</template>
</div>

<template is="dom-if" id="simple">
<div></div>
</template>
<div id="outerContainer">
<template is="dom-if" id="simple">
<x-client></x-client>
<x-client></x-client>
<x-client></x-client>
</template>

<div id="innerContainer">
</div>
</div>

<script>

Expand Down Expand Up @@ -518,19 +525,132 @@

});

suite('queueing race conditions', function() {
suite('attach/detach tests', function() {

test('remove, append domif', function(done) {
var domif = document.querySelector('#simple');
domif.if = true;
outerContainer.removeChild(domif);
setTimeout(function() {
var clients = outerContainer.querySelectorAll('x-client');
assert.equal(clients.length, 0);
outerContainer.appendChild(domif);
setTimeout(function() {
var clients = outerContainer.querySelectorAll('x-client');
assert.equal(clients[0].uid, 0);
assert.equal(clients[1].uid, 1);
assert.equal(clients[2].uid, 2);
assert.equal(clients[1].previousElementSibling, clients[0]);
assert.equal(clients[2].previousElementSibling, clients[1]);
assert.equal(domif.previousElementSibling, clients[2]);
done();
});
});
});

test('domif=true, attach, detach', function(done) {
test('move domif (clients persist)', function(done) {
var domif = document.querySelector('#simple');
domif.if = true;
document.body.appendChild(domif);
document.body.removeChild(domif);
Polymer.dom(innerContainer).appendChild(domif);
setTimeout(function() {
document.body.appendChild(domif);
var clients = innerContainer.querySelectorAll('x-client');
// Same clients as before since move happened in one turn
assert.equal(clients[0].uid, 0);
assert.equal(clients[1].uid, 1);
assert.equal(clients[2].uid, 2);
assert.equal(clients[1].previousElementSibling, clients[0]);
assert.equal(clients[2].previousElementSibling, clients[1]);
assert.equal(domif.previousElementSibling, clients[2]);
done();
});
});

test('remove, wait, append domif (clients recreated)', function(done) {
var domif = document.querySelector('#simple');
domif.if = true;
Polymer.dom(innerContainer).removeChild(domif);
setTimeout(function() {
var clients = innerContainer.querySelectorAll('x-client');
assert.equal(clients.length, 0);
Polymer.dom(innerContainer).appendChild(domif);
setTimeout(function() {
var clients = outerContainer.querySelectorAll('x-client');
// New clients since removed for a turn
assert.equal(clients[0].uid, 3);
assert.equal(clients[1].uid, 4);
assert.equal(clients[2].uid, 5);
assert.equal(clients[1].previousElementSibling, clients[0]);
assert.equal(clients[2].previousElementSibling, clients[1]);
assert.equal(domif.previousElementSibling, clients[2]);
done();
});
});
});

test('move host with domif (clients persist)', function(done) {
var host = document.createElement('x-host');
Polymer.dom(outerContainer).appendChild(host);
setTimeout(function() {
var clients = Polymer.dom(host.root).querySelectorAll('x-client');
// New clients created in host instance
assert.equal(clients[0].uid, 6);
assert.equal(clients[1].uid, 7);
assert.equal(clients[2].uid, 8);
assert.equal(clients[1].previousElementSibling, clients[0]);
assert.equal(clients[2].previousElementSibling, clients[1]);
assert.equal(host.$.domif.previousElementSibling, clients[2]);
Polymer.dom(innerContainer).appendChild(host);
setTimeout(function() {
var clients = Polymer.dom(host.root).querySelectorAll('x-client');
// Clients in removed host persist
assert.equal(clients[0].uid, 6);
assert.equal(clients[1].uid, 7);
assert.equal(clients[2].uid, 8);
assert.equal(clients[1].previousElementSibling, clients[0]);
assert.equal(clients[2].previousElementSibling, clients[1]);
assert.equal(host.$.domif.previousElementSibling, clients[2]);
done();
});
});
});

test('remove, wait, append host with domif (clients persist)', function(done) {
var host = document.createElement('x-host');
Polymer.dom(outerContainer).appendChild(host);
setTimeout(function() {
var clients = Polymer.dom(host.root).querySelectorAll('x-client');
// New clients created in host instance
assert.equal(clients[0].uid, 9);
assert.equal(clients[1].uid, 10);
assert.equal(clients[2].uid, 11);
assert.equal(clients[1].previousElementSibling, clients[0]);
assert.equal(clients[2].previousElementSibling, clients[1]);
assert.equal(host.$.domif.previousElementSibling, clients[2]);
Polymer.dom(outerContainer).removeChild(host);
setTimeout(function() {
// Clients in removed host persist
assert.equal(clients[0].uid, 9);
assert.equal(clients[1].uid, 10);
assert.equal(clients[2].uid, 11);
assert.equal(clients[1].previousElementSibling, clients[0]);
assert.equal(clients[2].previousElementSibling, clients[1]);
assert.equal(host.$.domif.previousElementSibling, clients[2]);
Polymer.dom(innerContainer).appendChild(host);
setTimeout(function() {
// Clients in removed host persist
var clients = Polymer.dom(host.root).querySelectorAll('x-client');
assert.equal(clients[0].uid, 9);
assert.equal(clients[1].uid, 10);
assert.equal(clients[2].uid, 11);
assert.equal(clients[1].previousElementSibling, clients[0]);
assert.equal(clients[2].previousElementSibling, clients[1]);
assert.equal(host.$.domif.previousElementSibling, clients[2]);
done();
});
});
});
});

});


Expand Down

0 comments on commit bab847a

Please sign in to comment.