Skip to content

Commit add7784

Browse files
committed
Ensure properties are set one-by-one at startup.
1 parent 4658b08 commit add7784

File tree

4 files changed

+126
-30
lines changed

4 files changed

+126
-30
lines changed

lib/mixins/property-effects.js

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -622,7 +622,9 @@ function applyBindingValue(inst, node, binding, part, value) {
622622
inst._enqueueClient(node);
623623
}
624624
}
625-
} else {
625+
} else if (!legacyNoBatch || inst.__dataReady) {
626+
// In legacy no-batching mode, bindings applied before dataReady are
627+
// equivalent to the "apply config" phase, which only set managed props
626628
inst._setUnmanagedPropertyToNode(node, prop, value);
627629
}
628630
}
@@ -1555,7 +1557,30 @@ export const PropertyEffects = dedupingMixin(superClass => {
15551557
*/
15561558
_flushProperties() {
15571559
this.__dataCounter++;
1558-
super._flushProperties();
1560+
if (legacyNoBatch && !this.__dataReady) {
1561+
// The first time this element is being flushed, propagate pending
1562+
// data down the tree first; this approximates 1.x's distributeConfig
1563+
this._propagatePropertyChanges(this.__dataPending, {}, false);
1564+
// Flushing clients causes this element to become __dataReady
1565+
this._flushClients();
1566+
// Capture element data and flush properties one-by one to defeat
1567+
// Polymer 2.x's batched _propertiesChanged scheme; this approximates
1568+
// 1.x's applyConfig
1569+
const changedProps = this.__data;
1570+
const notify = this.__dataToNotify;
1571+
this.__data = {};
1572+
this.__dataPending = this.__dataToNotify = null;
1573+
for (let prop in changedProps) {
1574+
const value = changedProps[prop];
1575+
this.__data[prop] = value;
1576+
this.__dataPending = {[prop]: value};
1577+
this.__dataOld = {};
1578+
this.__dataToNotify = {[prop]: notify && notify[prop]};
1579+
super._flushProperties();
1580+
}
1581+
} else {
1582+
super._flushProperties();
1583+
}
15591584
this.__dataCounter--;
15601585
}
15611586

@@ -1698,12 +1723,21 @@ export const PropertyEffects = dedupingMixin(superClass => {
16981723
// ----------------------------
16991724
let hasPaths = this.__dataHasPaths;
17001725
this.__dataHasPaths = false;
1726+
let notifyProps;
17011727
// Compute properties
1702-
runComputedEffects(this, changedProps, oldProps, hasPaths);
1703-
// Clear notify properties prior to possible reentry (propagate, observe),
1704-
// but after computing effects have a chance to add to them
1705-
let notifyProps = this.__dataToNotify;
1706-
this.__dataToNotify = null;
1728+
if (legacyNoBatch) {
1729+
// When not batching, computed effects may re-enter, so capture
1730+
// notifying properties early
1731+
notifyProps = this.__dataToNotify;
1732+
this.__dataToNotify = null;
1733+
runEffects(this, this[TYPES.COMPUTE], changedProps, oldProps, hasPaths);
1734+
} else {
1735+
runComputedEffects(this, changedProps, oldProps, hasPaths);
1736+
// Clear notify properties prior to possible reentry (propagate, observe),
1737+
// but after computing effects have a chance to add to them
1738+
notifyProps = this.__dataToNotify;
1739+
this.__dataToNotify = null;
1740+
}
17071741
// Propagate properties to clients
17081742
this._propagatePropertyChanges(changedProps, oldProps, hasPaths);
17091743
// Flush clients

test/runner.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
'unit/template-stamp.html',
3232
'unit/strict-template-policy.html',
3333
'unit/property-effects.html',
34+
'unit/property-effects.html?legacyUndefined=true',
35+
'unit/property-effects.html?legacyUndefined=true&legacyNoBatch=true',
3436
'unit/property-effects-template.html',
3537
'unit/path-effects.html',
3638
'unit/shady.html',

test/unit/property-effects-elements.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,9 @@ Polymer({
298298
},
299299

300300
$computeTrickyFunctionFromLiterals: function(num, str) {
301-
return this.computeFromLiterals(num, str);
301+
assert.equal(num, 3);
302+
assert.equal(str, 'foo');
303+
return num + str;
302304
},
303305

304306
computeFromTrickyLiterals: function(a, b) {

test/unit/property-effects.html

Lines changed: 80 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,13 @@
2626
<script type="module">
2727
import './property-effects-elements.js';
2828
import { Polymer, html } from '../../polymer-legacy.js';
29-
import { setSanitizeDOMValue, sanitizeDOMValue, setLegacyOptimizations } from '../../lib/utils/settings.js';
29+
import { setSanitizeDOMValue, sanitizeDOMValue, setLegacyOptimizations, legacyUndefined, setLegacyUndefined, legacyNoBatch, setLegacyNoBatch } from '../../lib/utils/settings.js';
3030
import { PropertyEffects } from '../../lib/mixins/property-effects.js';
3131

32+
setLegacyUndefined(Boolean(window.location.search.match('legacyUndefined')));
33+
setLegacyNoBatch(Boolean(window.location.search.match('legacyNoBatch')));
34+
35+
3236
suite('single-element binding effects', function() {
3337

3438
var el;
@@ -1234,7 +1238,9 @@
12341238
el.cpnd3 = {prop: 'cpnd3'};
12351239
assert.equal(el.$.boundProps.prop1, 'cpnd1cpnd2cpnd3');
12361240
el.cpnd4 = 'cpnd4';
1237-
assert.equal(el.$.boundProps.prop1, 'cpnd1cpnd2cpnd3literalComputedcpnd4');
1241+
assert.equal(el.$.boundProps.prop1,
1242+
legacyUndefined ?
1243+
'cpnd1cpnd2cpnd3' : 'cpnd1cpnd2cpnd3literalComputedcpnd4');
12381244
el.cpnd5 = 'cpnd5';
12391245
assert.equal(el.$.boundProps.prop1, 'cpnd1cpnd2cpnd3literalComputedcpnd5cpnd4');
12401246
});
@@ -1265,7 +1271,9 @@
12651271
el.cpnd3 = {prop: 'cpnd3'};
12661272
assert.equal(el.$.boundChild.getAttribute('compoundAttr1'), 'cpnd1cpnd2cpnd3');
12671273
el.cpnd4 = 'cpnd4';
1268-
assert.equal(el.$.boundChild.getAttribute('compoundAttr1'), 'cpnd1cpnd2cpnd3literalComputedcpnd4');
1274+
assert.equal(el.$.boundChild.getAttribute('compoundAttr1'),
1275+
legacyUndefined ?
1276+
'cpnd1cpnd2cpnd3' : 'cpnd1cpnd2cpnd3literalComputedcpnd4');
12691277
el.cpnd5 = 'cpnd5';
12701278
assert.equal(el.$.boundChild.getAttribute('compoundAttr1'), 'cpnd1cpnd2cpnd3literalComputedcpnd5cpnd4');
12711279
});
@@ -1302,7 +1310,9 @@
13021310
el.cpnd3 = {prop: 'cpnd3'};
13031311
assert.equal(el.$.compound1.textContent, 'cpnd1cpnd2cpnd3');
13041312
el.cpnd4 = 'cpnd4';
1305-
assert.equal(el.$.compound1.textContent, 'cpnd1cpnd2cpnd3literalComputedcpnd4');
1313+
assert.equal(el.$.compound1.textContent,
1314+
legacyUndefined ?
1315+
'cpnd1cpnd2cpnd3' : 'cpnd1cpnd2cpnd3literalComputedcpnd4');
13061316
el.cpnd5 = 'cpnd5';
13071317
assert.equal(el.$.compound1.textContent, 'cpnd1cpnd2cpnd3literalComputedcpnd5cpnd4');
13081318
// Once the binding evaluates back to '', it will in fact be ''
@@ -1770,29 +1780,57 @@
17701780
customElements.define('pe-method-observer', TestClass);
17711781
let el = document.createElement('pe-method-observer');
17721782
document.body.appendChild(el);
1773-
assert.equal(el.observer.callCount, 1);
1774-
assert.deepEqual(el.observer.getCall(0).args, [undefined, undefined]);
1783+
if (legacyUndefined) {
1784+
assert.equal(el.observer.callCount, 0);
1785+
} else {
1786+
assert.equal(el.observer.callCount, 1);
1787+
assert.deepEqual(el.observer.getCall(0).args, [undefined, undefined]);
1788+
}
17751789
assert.equal(el.pcSpy.callCount, 1);
17761790
el.a = 'a';
1777-
assert.equal(el.observer.callCount, 2);
1778-
assert.deepEqual(el.observer.getCall(1).args, ['a', undefined]);
1791+
if (legacyUndefined) {
1792+
assert.equal(el.observer.callCount, 0);
1793+
} else {
1794+
assert.equal(el.observer.callCount, 2);
1795+
assert.deepEqual(el.observer.getCall(1).args, ['a', undefined]);
1796+
}
17791797
assert.equal(el.pcSpy.callCount, 2);
17801798
el.b = {c: 'c'};
1781-
assert.equal(el.observer.callCount, 3);
1782-
assert.deepEqual(el.observer.getCall(2).args, ['a', 'c']);
1799+
if (legacyUndefined) {
1800+
assert.equal(el.observer.callCount, 1);
1801+
assert.deepEqual(el.observer.getCall(0).args, ['a', 'c']);
1802+
} else {
1803+
assert.equal(el.observer.callCount, 3);
1804+
assert.deepEqual(el.observer.getCall(2).args, ['a', 'c']);
1805+
}
17831806
assert.equal(el.pcSpy.callCount, 3);
17841807
el.setProperties({
17851808
a: 'A',
17861809
b: {c: 'C'}
17871810
});
1788-
assert.equal(el.observer.callCount, 4);
1789-
assert.deepEqual(el.observer.getCall(3).args, ['A', 'C']);
1790-
assert.equal(el.pcSpy.callCount, 4);
1811+
if (legacyNoBatch && legacyUndefined) {
1812+
assert.equal(el.observer.callCount, 3);
1813+
assert.deepEqual(el.observer.getCall(1).args, ['A', 'c']);
1814+
assert.deepEqual(el.observer.getCall(2).args, ['A', 'C']);
1815+
assert.equal(el.pcSpy.callCount, 5);
1816+
} else if (legacyUndefined) {
1817+
assert.equal(el.observer.callCount, 2);
1818+
assert.deepEqual(el.observer.getCall(1).args, ['A', 'C']);
1819+
assert.equal(el.pcSpy.callCount, 4);
1820+
} else {
1821+
assert.equal(el.observer.callCount, 4);
1822+
assert.deepEqual(el.observer.getCall(3).args, ['A', 'C']);
1823+
assert.equal(el.pcSpy.callCount, 4);
1824+
}
17911825

17921826
el.observer = sinon.spy();
17931827
assert.equal(el.observer.callCount, 1);
17941828
assert.deepEqual(el.observer.getCall(0).args, ['A', 'C']);
1795-
assert.equal(el.pcSpy.callCount, 5);
1829+
if (legacyNoBatch && legacyUndefined) {
1830+
assert.equal(el.pcSpy.callCount, 6);
1831+
} else {
1832+
assert.equal(el.pcSpy.callCount, 5);
1833+
}
17961834
document.body.removeChild(el);
17971835
});
17981836

@@ -1810,23 +1848,43 @@
18101848
assert.equal(el.compute.callCount, 0);
18111849
assert.equal(el.pcSpy.callCount, 0);
18121850
el.a = 'a';
1813-
assert.equal(el.compute.callCount, 1);
1814-
assert.deepEqual(el.compute.getCall(0).args, ['a', undefined]);
1815-
assert.equal(el.prop, 'aundefined');
1851+
if (legacyUndefined) {
1852+
assert.equal(el.compute.callCount, 0);
1853+
} else {
1854+
assert.equal(el.compute.callCount, 1);
1855+
assert.deepEqual(el.compute.getCall(0).args, ['a', undefined]);
1856+
assert.equal(el.prop, 'aundefined');
1857+
}
18161858
assert.equal(el.pcSpy.callCount, 1);
18171859
el.b = {c: 'c'};
1818-
assert.equal(el.compute.callCount, 2);
1819-
assert.deepEqual(el.compute.getCall(1).args, ['a', 'c']);
1860+
if (legacyUndefined) {
1861+
assert.equal(el.compute.callCount, 1);
1862+
assert.deepEqual(el.compute.getCall(0).args, ['a', 'c']);
1863+
} else {
1864+
assert.equal(el.compute.callCount, 2);
1865+
assert.deepEqual(el.compute.getCall(1).args, ['a', 'c']);
1866+
}
18201867
assert.equal(el.prop, 'ac');
18211868
assert.equal(el.pcSpy.callCount, 2);
18221869
el.setProperties({
18231870
a: 'A',
18241871
b: {c: 'C'}
18251872
});
1826-
assert.equal(el.compute.callCount, 3);
1827-
assert.deepEqual(el.compute.getCall(2).args, ['A', 'C']);
1873+
if (legacyNoBatch && legacyUndefined) {
1874+
assert.equal(el.compute.callCount, 3);
1875+
assert.deepEqual(el.compute.getCall(1).args, ['A', 'c']);
1876+
assert.deepEqual(el.compute.getCall(2).args, ['A', 'C']);
1877+
assert.equal(el.pcSpy.callCount, 4);
1878+
} else if (legacyUndefined) {
1879+
assert.equal(el.compute.callCount, 2);
1880+
assert.deepEqual(el.compute.getCall(1).args, ['A', 'C']);
1881+
assert.equal(el.pcSpy.callCount, 3);
1882+
} else {
1883+
assert.equal(el.compute.callCount, 3);
1884+
assert.deepEqual(el.compute.getCall(2).args, ['A', 'C']);
1885+
assert.equal(el.pcSpy.callCount, 3);
1886+
}
18281887
assert.equal(el.prop, 'AC');
1829-
assert.equal(el.pcSpy.callCount, 3);
18301888
document.body.removeChild(el);
18311889
});
18321890

0 commit comments

Comments
 (0)