Skip to content

Commit

Permalink
Merge pull request #156 from canjs/bind-reconnected-nodes
Browse files Browse the repository at this point in the history
Re-bind placeholder nodes after they reconnect to the document
  • Loading branch information
bmomberger-bitovi authored Jan 6, 2020
2 parents 95acaff + 0d970dc commit 6819e3a
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 19 deletions.
48 changes: 36 additions & 12 deletions lib/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,23 @@ var canReflect = require("can-reflect");
var canReflectDeps = require('can-reflect-dependencies');
var parser = require('can-view-parser');
var canDev = require("can-log/dev/dev");
var isConnected = require("can-dom-mutate/-is-connected");

var setElementSymbol = canSymbol.for("can.setElement");
var elementSymbol = canSymbol.for("can.element");

function ListenUntilRemovedAndInitialize(observable, handler, placeholder, queueName, handlerName, afterTeardownNodeRemoved) {
function ListenUntilRemovedAndInitialize(
observable,
handler,
placeholder,
queueName,
handlerName
) {
this.observable = observable;
this.handler = handler;
this.placeholder = placeholder;
this.queueName = queueName;
this.handler[elementSymbol] = placeholder;
this.afterTeardownNodeRemoved = afterTeardownNodeRemoved;

if( observable[setElementSymbol] ) {
observable[setElementSymbol](placeholder);
Expand All @@ -40,24 +46,42 @@ function ListenUntilRemovedAndInitialize(observable, handler, placeholder, queue
value: handlerName,
});

canReflectDeps.addMutatedBy(placeholder, observable);
}
//!steal-remove-end

this.teardownNodeRemoved = domMutate.onNodeRemoved(placeholder,
this.setup();
}
ListenUntilRemovedAndInitialize.prototype.setup = function() {
// reinsertion case, not applicable during initial setup
if(this.setupNodeReinserted) {
// do not set up again if disconnected
if(!isConnected.isConnected(this.placeholder)) {
return;
}
this.setupNodeReinserted();
}
this.teardownNodeRemoved = domMutate.onNodeRemoved(this.placeholder,
this.teardown.bind(this));

canReflect.onValue(observable, handler, queueName);

// data = live.listen(parentNode, compute, liveHTMLUpdateHTML);
handler( canReflect.getValue(observable) );
}
ListenUntilRemovedAndInitialize.prototype.teardown = function(){
this.teardownNodeRemoved();
//!steal-remove-start
if(process.env.NODE_ENV !== 'production') {
canReflectDeps.addMutatedBy(this.placeholder, this.observable);
}
//!steal-remove-end

canReflect.onValue(this.observable, this.handler, this.queueName);
this.handler( canReflect.getValue(this.observable) );

if (this.afterTeardownNodeRemoved) {
this.afterTeardownNodeRemoved();
};
ListenUntilRemovedAndInitialize.prototype.teardown = function(){
// do not teardown if still connected.
if(isConnected.isConnected(this.placeholder)) {
return;
}
this.teardownNodeRemoved();
this.setupNodeReinserted = domMutate.onNodeInserted(this.placeholder,
this.setup.bind(this));

//!steal-remove-start
if(process.env.NODE_ENV !== 'production') {
Expand Down
7 changes: 1 addition & 6 deletions lib/html.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
var makeFragment = require('can-fragment');
var canReflect = require('can-reflect');
var canSymbol = require("can-symbol");
var queues = require("can-queues");
var helpers = require('./helpers');
var getDocument = require('can-globals/document/document');

Expand Down Expand Up @@ -65,10 +64,6 @@ module.exports = function(el, compute, viewInsertSymbolOptions) {
}
//!steal-remove-end

var doNotUpdateRange = function() {
queues.domQueue.dequeue(updateRange);
};

if (el.nodeType !== Node.COMMENT_NODE) {
var commentFrag = makeCommentFragment(observableName);
var startCommentNode = commentFrag.firstChild;
Expand Down Expand Up @@ -109,6 +104,6 @@ module.exports = function(el, compute, viewInsertSymbolOptions) {
},
range.start,
"dom",
"live.html replace::" + observableName, doNotUpdateRange);
"live.html replace::" + observableName);

};
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"can-attribute-observable": "^2.0.0",
"can-child-nodes": "^1.0.0",
"can-diff": "^1.5.0",
"can-dom-mutate": "^2.0.0",
"can-dom-mutate": "^2.0.8",
"can-fragment": "^1.0.0",
"can-observation": "^4.2.0",
"can-queues": "^1.3.0",
Expand Down
56 changes: 56 additions & 0 deletions test/html-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,23 @@ var canSymbol = require('can-symbol');
var fragment = require("can-fragment");
var queues = require("can-queues");
var domMutateNode = require("can-dom-mutate/node/node");
var canGlobals = require("can-globals");

QUnit.module("can-view-live.html");

var afterMutation = function(cb) {
var doc = canGlobals.getKeyValue("document");
var div = doc.createElement("div");
var insertionDisposal = domMutate.onNodeInsertion(div, function(){
insertionDisposal();
doc.body.removeChild(div);
setTimeout(cb, 5);
});
setTimeout(function(){
domMutateNode.appendChild.call(doc.body, div);
}, 10);
};

QUnit.test('basics', function(assert) {
var div = document.createElement('div'),
span = document.createElement('span');
Expand Down Expand Up @@ -274,3 +288,45 @@ QUnit.test("tearing down a .html inside another .html works", function(assert) {
assert.deepEqual(ifPersonFrag.childNodes.length, 3, "nodes torn down correctly");
assert.deepEqual(ifPersonFrag.childNodes[1].textContent, "", "placeholder text node correct");
});

QUnit.test('Live binding is restored when the placeholder is reconnected', function(assert) {
var done = assert.async();

var div = document.createElement('div'),
span = document.createElement('span');
document.getElementById("qunit-fixture").appendChild(div);

div.appendChild(span);
var items = new DefineList([
'one',
'two'
]);

var html = new Observation(function itemsHTML() {
var html = '';
items.forEach(function (item) {
html += '<label>' + item + '</label>';
});
return html;
});
live.html(span, html);
assert.equal(div.getElementsByTagName('label').length, 2);
var commentChildren = [].slice.call(div.childNodes, 0).filter(function(node) {
return node.nodeType === Node.COMMENT_NODE;
});
div.removeChild(commentChildren[0]);
div.removeChild(commentChildren[1]);

afterMutation(function() {
items.push('three');

div.insertBefore(commentChildren[0], div.firstChild);
div.appendChild(commentChildren[1]);

afterMutation(function() {
console.log( document.getElementById("qunit-fixture").innerHTML);
assert.equal(div.getElementsByTagName('label').length, 3);
done();
});
});
});
53 changes: 53 additions & 0 deletions test/text-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@ QUnit.module("can-view-live.text", {
}
});

var afterMutation = function(cb) {
var doc = canGlobals.getKeyValue("document");
var div = doc.createElement("div");
var insertionDisposal = domMutate.onNodeInsertion(div, function(){
insertionDisposal();
doc.body.removeChild(div);
setTimeout(cb, 5);
});
setTimeout(function(){
domMutateNode.appendChild.call(doc.body, div);
}, 10);
};

var esc = function(str) {
return str.replace(/</g, '&lt;')
Expand Down Expand Up @@ -117,6 +129,47 @@ testHelpers.dev.devOnlyTest('can-reflect-dependencies', function(assert) {
domMutateNode.removeChild.call(div.parentNode, div);
});

QUnit.test('Live binding is restored when the text node is reconnected', function(assert) {
var done = assert.async();

var div = document.createElement('div'),
textNode = document.createTextNode('');
this.fixture.appendChild(div);
div.appendChild(textNode);
var value = new SimpleObservable('one');
live.text(textNode, value);
assert.equal(div.innerHTML, 'one');
// case 1: don't run teardown due to synchronous reconnection
div.appendChild(textNode);

afterMutation(function() {
value.set('two');

assert.equal(div.innerHTML, 'two');

// case 2: teardown, then set up again after mutations.
div.removeChild(textNode);

afterMutation(function() {
// live binding is off while the node is disconnected.
value.set('three');
assert.equal(textNode.nodeValue, 'two');
div.appendChild(textNode);

afterMutation(function() {
// value is recomputed on reconnection.
assert.equal(div.innerHTML, 'three');

// live binding continues after reconnected.
value.set('four');
assert.equal(div.innerHTML, 'four');

done();
});
});
});
});

QUnit.test("Removing the documentElement tears down correctly", function(assert) {
var done = assert.async();
assert.expect(1);
Expand Down

0 comments on commit 6819e3a

Please sign in to comment.