Skip to content

Commit

Permalink
Merge pull request #1594 from Polymer/1500-kschaaf
Browse files Browse the repository at this point in the history
Wait until imports resolve to stamp. Fixes #1500
  • Loading branch information
Steve Orvell committed May 23, 2015
2 parents 65a9731 + b9c7a03 commit 549f991
Show file tree
Hide file tree
Showing 6 changed files with 289 additions and 8 deletions.
46 changes: 46 additions & 0 deletions src/lib/imports-status.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<!--
@license
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->

<script>

window.addEventListener('load', function() {
Polymer.ImportStatus._importsLoaded();
});

if (window.HTMLImports) {
HTMLImports.whenReady(function() {
Polymer.ImportStatus._importsLoaded();
});
}

Polymer.ImportStatus = {

_ready: false,

_callbacks: [],

whenLoaded: function(cb) {
if (this._ready) {
cb();
} else {
this._callbacks.push(cb);
}
},

_importsLoaded: function() {
this._ready = true;
this._callbacks.forEach(function(cb) {
cb();
});
this._callbacks = [];
}
};

</script>
52 changes: 45 additions & 7 deletions src/lib/template/dom-bind.html
Original file line number Diff line number Diff line change
Expand Up @@ -48,31 +48,69 @@
-->

<link rel="import" href="../imports-status.html">

<script>

Polymer({

/**
* Fired whenever DOM is stamped by this template (rendering
* will be deferred until all HTML imports have resolved).
*
* @event dom-change
*/

is: 'dom-bind',

extends: 'template',

created: function() {
// Ensure dom-bind doesn't stamp until all possible dependencies
// have resolved
Polymer.ImportStatus.whenLoaded(this._readySelf.bind(this));
},

_registerFeatures: function() {
this._prepExtends();
this._prepConstructor();
},

_finishDistribute: function() {
_insertChildren: function() {
var parentDom = Polymer.dom(Polymer.dom(this).parentNode);
parentDom.insertBefore(this.root, this);
},

_removeChildren: function() {
if (this._children) {
for (var i=0; i<this._children.length; i++) {
this.root.appendChild(this._children[i]);
}
}
},

_initFeatures: function() {
this._template = this;
this._prepAnnotations();
this._prepEffects();
this._prepBehaviors();
this._prepBindings();
Polymer.Base._initFeatures.call(this);
// defer _initFeatures and stamping until after attached, to support
// document.createElement('template', 'dom-bind') use case,
// where template content is filled in after creation
},

attached: function() {
if (!this._children) {
this._template = this;
this._prepAnnotations();
this._prepEffects();
this._prepBehaviors();
this._prepBindings();
Polymer.Base._initFeatures.call(this);
this._children = Array.prototype.slice.call(this.root.childNodes);
}
this._insertChildren();
this.fire('dom-change');
},

detached: function() {
this._removeChildren();
}

});
Expand Down
3 changes: 2 additions & 1 deletion test/runner.html
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@
'unit/custom-style.html',
'unit/dynamic-import.html',
'unit/dom-repeat.html',
'unit/dom-if.html'
'unit/dom-if.html',
'unit/dom-bind.html'
]);
</script>
</body>
Expand Down
10 changes: 10 additions & 0 deletions test/unit/dom-bind-elements1.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<script>
Polymer({
is: 'x-basic',
properties: {
notifyingvalue: {
notify: true
}
}
});
</script>
12 changes: 12 additions & 0 deletions test/unit/dom-bind-elements2.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<script>
console.log('x-needs-repeat loaded')
Polymer({
is: 'x-needs-host',
ready: function() {
if (!this.dataHost) {
throw "No dataHost at ready time";
}
this.config = this.dataHost.getAttribute('config');
}
});
</script>
174 changes: 174 additions & 0 deletions test/unit/dom-bind.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
<!doctype html>
<!--
@license
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<html>
<head>
<meta charset="utf-8">
<script src="../../../webcomponentsjs/webcomponents-lite.js"></script>
<script src="../../../web-component-tester/browser.js"></script>
<link rel="import" href="../../polymer.html">
<link rel="import" href="dom-bind-elements1.html">
</head>
<body>

<template is="dom-bind" id="decDomBind">
<x-basic id="decEl1" value="{{value}}" notifyingvalue="{{nvalue}}" on-custom="handleEvent" computed="{{compute(dep)}}"></x-basic>
<x-basic id="decEl2" value="{{value}}" notifyingvalue="{{nvalue}}"></x-basic>
</template>

<div id="container">
</div>

<template is="dom-bind" id="timingDomBind" config="config">
<x-needs-host id="needsHost"></x-needs-host>
</template>

<script>

suite('declarative dom-bind', function() {

var domBind;
var el1;
var el2;

setup(function() {
domBind = decDomBind;
el1 = decEl1;
el2 = decEl2;
});

test('value binds top-down', function() {
domBind.value = 'foo';
assert.equal(el1.value, 'foo');
assert.equal(el2.value, 'foo');
});

test('notifyingvalue binds from child to child', function() {
el1.notifyingvalue = 'bar';
assert.equal(domBind.nvalue, 'bar');
assert.equal(el2.notifyingvalue, 'bar');
});

test('event listener fires', function() {
var count = 0;
domBind.handleEvent = function() {
count++;
};
el1.fire('custom');
assert.equal(count, 1);
});

test('inline function runs', function() {
domBind.compute = function(val) {
return val * 10;
};
domBind.dep = 5;
assert.equal(el1.computed, 50);
});

});

suite('imperative dom-bind', function() {

var domBind;
var el1;
var el2;

setup(function() {
domBind = document.createElement('template', 'dom-bind');
var doc = domBind.content.ownerDocument;
el1 = doc.createElement('x-basic');
el1.setAttribute('id', 'impEl1');
el1.setAttribute('value', '{{value}}');
el1.setAttribute('notifyingvalue', '{{nvalue}}');
el1.setAttribute('on-custom', 'handleEvent');
el1.setAttribute('computed', '{{compute(dep)}}');
el2 = doc.createElement('x-basic');
el2.setAttribute('id', 'impEl2');
el2.setAttribute('value', '{{value}}');
el2.setAttribute('notifyingvalue', '{{nvalue}}');
domBind.content.appendChild(el1);
domBind.content.appendChild(el2);
document.body.appendChild(domBind);
CustomElements.takeRecords();
el1 = domBind.$.impEl1;
el2 = domBind.$.impEl2;
});

teardown(function() {
if (domBind.parentElement) {
domBind.parentElement.removeChild(domBind);
}
});

test('value binds top-down', function() {
domBind.value = 'foo';
assert.equal(el1.value, 'foo');
assert.equal(el2.value, 'foo');
});

test('notifyingvalue binds from child to child', function() {
el1.notifyingvalue = 'bar';
assert.equal(domBind.nvalue, 'bar');
assert.equal(el2.notifyingvalue, 'bar');
});

test('event listener fires', function() {
var count = 0;
domBind.handleEvent = function() {
count++;
};
el1.fire('custom');
assert.equal(count, 1);
});

test('inline function runs', function() {
domBind.compute = function(val) {
return val * 10;
};
domBind.dep = 5;
assert.equal(el1.computed, 50);
});

test('move dom-bind', function() {
domBind.parentElement.removeChild(domBind);
// TODO(kschaaf): detached/attached not called if takeRecords isn't
// called between remove & insert on polyfill... seems bad?
CustomElements.takeRecords();
container.appendChild(domBind);
CustomElements.takeRecords();
assert.equal(container.firstElementChild, el1);
assert.equal(container.firstElementChild.nextElementSibling, el2);
});

test('remove dom-bind', function() {
domBind.parentElement.removeChild(domBind);
CustomElements.takeRecords();
assert(!document.body.contains(el1));
assert(!document.body.contains(el2));
});

});

suite('timing', function() {

test('late-loaded import should block stamping', function() {
assert.equal(needsHost.config, 'config');
});

});


</script>

<link rel="import" href="dom-bind-elements2.html">
<link rel="import" href="should-404.html">
</body>
</html>

0 comments on commit 549f991

Please sign in to comment.