Skip to content

Commit

Permalink
Adds support for imperatively created elements to `legacyNoObservedAt…
Browse files Browse the repository at this point in the history
…tributes`

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.
  • Loading branch information
Steven Orvell committed Jan 21, 2020
1 parent afdd911 commit 28f12ca
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 5 deletions.
24 changes: 19 additions & 5 deletions lib/legacy/legacy-element-mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ export const LegacyElementMixin = dedupingMixin((base) => {
// NOTE: Inlined for perf from version of DisableUpgradeMixin.
/** @type {boolean|undefined} */
this.__isUpgradeDisabled;
/** @type {boolean|undefined} */
this.__needsAttributesAtConnected;
}

/**
Expand Down Expand Up @@ -196,6 +198,9 @@ export const LegacyElementMixin = dedupingMixin((base) => {
* @override
*/
connectedCallback() {
if (this.__needsAttributesAtConnected) {
this._takeAttributes();
}
// NOTE: Inlined for perf from version of DisableUpgradeMixin.
if (!this.__isUpgradeDisabled) {
super.connectedCallback();
Expand Down Expand Up @@ -301,11 +306,12 @@ export const LegacyElementMixin = dedupingMixin((base) => {
this.root = /** @type {HTMLElement} */(this);
this.created();
// Pull all attribute values 1x if `legacyNoObservedAttributes` is set.
if (legacyNoObservedAttributes && this.hasAttributes()) {
const a = this.attributes;
for (let i=0, l=a.length; i < l; i++) {
const attr = a[i];
this.__attributeReaction(attr.name, null, attr.value);
if (legacyNoObservedAttributes) {
if (this.hasAttributes()) {
this._takeAttributes();
// Element created from scratch or parser generated
} else if (!this.parentNode) {
this.__needsAttributesAtConnected = true;
}
}
// Ensure listeners are applied immediately so that they are
Expand All @@ -316,6 +322,14 @@ export const LegacyElementMixin = dedupingMixin((base) => {
}
}

_takeAttributes() {
const a = this.attributes;
for (let i=0, l=a.length; i < l; i++) {
const attr = a[i];
this.__attributeReaction(attr.name, null, attr.value);
}
}

/**
* Called automatically when an element is initializing.
* Users may override this method to perform class registration time
Expand Down
21 changes: 21 additions & 0 deletions test/unit/legacy-noattributes.html
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,27 @@
assert.equal(el.camelCase, 'camelCase');
});

test('imperative creation', () => {
el = document.createElement('x-attrs');
// Create attributes in an unpatched way.
const a = document.createAttribute('foo');
a.value = 'foo';
el.setAttributeNode(a);
const b = document.createAttribute('bar');
b.value = 'bar';
el.setAttributeNode(b);
const c = document.createAttribute('camel-case');
c.value = 'camelCase';
el.setAttributeNode(c);
document.body.appendChild(el);
assert.equal(el.foo, 'foo');
assert.equal(el.$.child1.getAttribute('foo'), 'x-attrs.foo');
assert.equal(el.$.child1.foo, "x-attrs.foo");
assert.equal(el.$.child1.$.content.textContent, 'x-attrs.foo');
assert.equal(el.camelCase, 'camelCase');
document.body.removeChild(el);
});

test('created called before attributeChanged', () => {
assert.isTrue(el.wasCreatedInAttributeChanged);
});
Expand Down

0 comments on commit 28f12ca

Please sign in to comment.