diff --git a/lib/mixins/element-mixin.html b/lib/mixins/element-mixin.html
index a3bb9c9eec..aae9c992e9 100644
--- a/lib/mixins/element-mixin.html
+++ b/lib/mixins/element-mixin.html
@@ -656,6 +656,10 @@
hostStack.endHosting(this);
this.$ = this.root.$;
}
+ // The super.ready here makes this element able to observe data changes.
+ // We must wait to do this until after client dom is created/attached
+ // so that any notifications fired during this process are not handled
+ // before all clients are ready.
super.ready();
}
@@ -669,10 +673,10 @@
* @override
*/
_readyClients() {
- super._readyClients();
if (this._template) {
this.root = this._attachDom(this.root);
}
+ super._readyClients();
}
diff --git a/lib/mixins/property-effects.html b/lib/mixins/property-effects.html
index 2321826b7e..9cc1d0adcc 100644
--- a/lib/mixins/property-effects.html
+++ b/lib/mixins/property-effects.html
@@ -227,8 +227,11 @@
}
}
// Flush host if we actually notified and host was batching
+ // And the host has already initialized clients; this prevents
+ // an issue with a host observing data changes before clients are ready.
let host;
- if (notified && (host = inst.__dataHost) && host._flushProperties) {
+ if (notified && (host = inst.__dataHost) && host._flushProperties
+ && host.__dataClientsInitialized) {
host._flushProperties();
}
}
@@ -1509,6 +1512,11 @@
if (!this.__dataClientsInitialized) {
this._readyClients();
}
+ // Before ready, client notifications do not trigger _flushProperties.
+ // Therefore a flush is necessary here if data has been set.
+ if (this.__dataPending) {
+ this._flushProperties();
+ }
}
/**
diff --git a/test/unit/property-effects-elements.html b/test/unit/property-effects-elements.html
index 7005ec55b3..aeb74816c1 100644
--- a/test/unit/property-effects-elements.html
+++ b/test/unit/property-effects-elements.html
@@ -184,6 +184,9 @@
};
this.titleChanged = sinon.spy();
},
+ ready: function() {
+ this.isReady = true;
+ },
clearObserverCounts: function() {
for (var i in this.observerCounts) {
this.observerCounts[i] = 0;
@@ -344,7 +347,8 @@
'boundcomputedvalueChanged(boundcomputedvalue)',
'boundcomputednotifyingvalueChanged(boundcomputednotifyingvalue)',
'boundreadonlyvalueChanged(boundreadonlyvalue)',
- 'boundCustomNotifyingValueChanged(boundCustomNotifyingValue)'
+ 'boundCustomNotifyingValueChanged(boundCustomNotifyingValue)',
+ 'boundnotifyingvalueWithDefaultChanged(boundnotifyingvalueWithDefault)'
],
properties: {
a: {
@@ -367,7 +371,8 @@
boundcomputedvalueChanged: 0,
boundcomputednotifyingvalueChanged: 0,
boundreadonlyvalueChanged: 0,
- boundCustomNotifyingValueChanged: 0
+ boundCustomNotifyingValueChanged: 0,
+ boundnotifyingvalueWithDefault: 0
};
},
computeComputedValue: function(a, b) {
@@ -378,23 +383,39 @@
this.observerCounts[i] = 0;
}
},
+ assertClientsReady: function() {
+ assert.isTrue(this.$.basic1.isReady, 'basic1 not `ready` by observer time');
+ assert.isTrue(this.$.basic2.isReady, 'basic2 element not `ready` by observer time');
+ assert.isTrue(this.$.basic3.isReady, 'basic3 element not `ready` by observer time');
+ assert.isTrue(this.$.basic4.isReady, 'basic4 element not `ready` by observer time');
+ },
boundvalueChanged: function() {
+ this.assertClientsReady();
this.observerCounts.boundvalueChanged++;
},
boundnotifyingvalueChanged: function() {
+ this.assertClientsReady();
this.observerCounts.boundnotifyingvalueChanged++;
},
boundcomputedvalueChanged: function() {
+ this.assertClientsReady();
this.observerCounts.boundcomputedvalueChanged++;
},
boundcomputednotifyingvalueChanged: function() {
+ this.assertClientsReady();
this.observerCounts.boundcomputednotifyingvalueChanged++;
},
boundreadonlyvalueChanged: function() {
+ this.assertClientsReady();
this.observerCounts.boundreadonlyvalueChanged++;
},
boundCustomNotifyingValueChanged: function() {
+ this.assertClientsReady();
this.observerCounts.boundCustomNotifyingValueChanged++;
+ },
+ boundnotifyingvalueWithDefaultChanged: function() {
+ this.assertClientsReady();
+ this.observerCounts.boundnotifyingvalueWithDefault++;
}
});