Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Components do not respect observedAttributes/attributeChangedCallback #2972

Open
nolanlawson opened this issue Aug 2, 2022 · 2 comments
Open
Labels

Comments

@nolanlawson
Copy link
Contributor

nolanlawson commented Aug 2, 2022

Description

LWC components do not respect observedAttributes or attributeChangedCallback. attributeChangedCallback is never called.

Create an LWC component:

import { LightningElement, api } from 'lwc'

export default class extends LightningElement {
  static observedAttributes = ['foo']

  @api changes = []

  attributeChangedCallback(name, oldValue, newValue) {
    this.changes.push({ name, oldValue, newValue })
  }
}

The attributes are never observed:

const elm = createElement('x-observes', { is: Observes })
document.body.appendChild(elm)

elm.setAttribute('foo', 'bar')
elm.removeAttribute('foo')

// attributeChangedCallback never called
console.log(elm.changes) // []

Steps to Reproduce

Repro (look in the console)

Expected Results

Attributes are observed

Actual Results

Attributes are not observed

Possible Solution

Use properties and @track instead of attributes

@git2gus
Copy link

git2gus bot commented Aug 2, 2022

This issue has been linked to a new work item: W-11534181

@nolanlawson
Copy link
Contributor Author

CustomElementConstructor seems to work similarly, but slightly differently:

  • static observedAttributes is ignored – only the @api props are considered
  • attributeChangedCallback is also ignored
  • However, the framework registers an attributeChangedCallback to decide when to set a prop:

return function attributeChangedCallback(
this: HTMLElement,
attrName: string,
oldValue: string,
newValue: string
) {
if (oldValue === newValue) {
// Ignore same values.
return;
}
const propName = attributeToPropMap[attrName];
if (isUndefined(propName)) {
if (!isUndefined(superAttributeChangedCallback)) {
// delegate unknown attributes to the super.
// Typescript does not like it when you treat the `arguments` object as an array
// @ts-ignore type-mismatch
superAttributeChangedCallback.apply(this, arguments);
}
return;
}
if (!isAttributeLocked(this, attrName)) {
// Ignore changes triggered by the engine itself during:
// * diffing when public props are attempting to reflect to the DOM
// * component via `this.setAttribute()`, should never update the prop
// Both cases, the setAttribute call is always wrapped by the unlocking of the
// attribute to be changed
return;
}
// Reflect attribute change to the corresponding property when changed from outside.
(this as any)[propName] = newValue;
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant