Skip to content

Commit 28f12ca

Browse files
author
Steven Orvell
committed
Adds support for imperatively created elements to legacyNoObservedAttributes
Using this flag previously read attributes in the element constructor (in addition to patching setAttribute). This covers use cases except imperative or parser created custom elements (not upgraded). In that case the element will not have attributes in the constructor. This is addressed here by checking if the element has a parentNode (which will also not be true in these cases) and if so, records attributes at connected time. This is not always done to avoid having to filter out changes made by bindings.
1 parent afdd911 commit 28f12ca

File tree

2 files changed

+40
-5
lines changed

2 files changed

+40
-5
lines changed

lib/legacy/legacy-element-mixin.js

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ export const LegacyElementMixin = dedupingMixin((base) => {
101101
// NOTE: Inlined for perf from version of DisableUpgradeMixin.
102102
/** @type {boolean|undefined} */
103103
this.__isUpgradeDisabled;
104+
/** @type {boolean|undefined} */
105+
this.__needsAttributesAtConnected;
104106
}
105107

106108
/**
@@ -196,6 +198,9 @@ export const LegacyElementMixin = dedupingMixin((base) => {
196198
* @override
197199
*/
198200
connectedCallback() {
201+
if (this.__needsAttributesAtConnected) {
202+
this._takeAttributes();
203+
}
199204
// NOTE: Inlined for perf from version of DisableUpgradeMixin.
200205
if (!this.__isUpgradeDisabled) {
201206
super.connectedCallback();
@@ -301,11 +306,12 @@ export const LegacyElementMixin = dedupingMixin((base) => {
301306
this.root = /** @type {HTMLElement} */(this);
302307
this.created();
303308
// Pull all attribute values 1x if `legacyNoObservedAttributes` is set.
304-
if (legacyNoObservedAttributes && this.hasAttributes()) {
305-
const a = this.attributes;
306-
for (let i=0, l=a.length; i < l; i++) {
307-
const attr = a[i];
308-
this.__attributeReaction(attr.name, null, attr.value);
309+
if (legacyNoObservedAttributes) {
310+
if (this.hasAttributes()) {
311+
this._takeAttributes();
312+
// Element created from scratch or parser generated
313+
} else if (!this.parentNode) {
314+
this.__needsAttributesAtConnected = true;
309315
}
310316
}
311317
// Ensure listeners are applied immediately so that they are
@@ -316,6 +322,14 @@ export const LegacyElementMixin = dedupingMixin((base) => {
316322
}
317323
}
318324

325+
_takeAttributes() {
326+
const a = this.attributes;
327+
for (let i=0, l=a.length; i < l; i++) {
328+
const attr = a[i];
329+
this.__attributeReaction(attr.name, null, attr.value);
330+
}
331+
}
332+
319333
/**
320334
* Called automatically when an element is initializing.
321335
* Users may override this method to perform class registration time

test/unit/legacy-noattributes.html

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,27 @@
108108
assert.equal(el.camelCase, 'camelCase');
109109
});
110110

111+
test('imperative creation', () => {
112+
el = document.createElement('x-attrs');
113+
// Create attributes in an unpatched way.
114+
const a = document.createAttribute('foo');
115+
a.value = 'foo';
116+
el.setAttributeNode(a);
117+
const b = document.createAttribute('bar');
118+
b.value = 'bar';
119+
el.setAttributeNode(b);
120+
const c = document.createAttribute('camel-case');
121+
c.value = 'camelCase';
122+
el.setAttributeNode(c);
123+
document.body.appendChild(el);
124+
assert.equal(el.foo, 'foo');
125+
assert.equal(el.$.child1.getAttribute('foo'), 'x-attrs.foo');
126+
assert.equal(el.$.child1.foo, "x-attrs.foo");
127+
assert.equal(el.$.child1.$.content.textContent, 'x-attrs.foo');
128+
assert.equal(el.camelCase, 'camelCase');
129+
document.body.removeChild(el);
130+
});
131+
111132
test('created called before attributeChanged', () => {
112133
assert.isTrue(el.wasCreatedInAttributeChanged);
113134
});

0 commit comments

Comments
 (0)