Skip to content

setAttribute() after mount does not update the property on plain reflect props #98

@DmitrySharabin

Description

@DmitrySharabin

Symptom

For a plain prop declared with reflect: true, calling el.setAttribute("v", "100") after the element has mounted does not update el.v. Pre-set attributes (set before append) parse correctly into the property; the regression only affects attribute changes after mount.

class C extends NudeElement {
  static props = { v: { type: Number, reflect: true } };
}
customElements.define("x-c", C);

let el = new C();
document.body.append(el);

el.v = 42;                        // works → el.getAttribute("v") === "42"
el.setAttribute("v", "100");
// expected: el.v === 100
// actual:   el.v === 42 (no attributeChangedCallback fired)

Root cause

Class.observedAttributes is wired via the static getter installed by first_constructor_static (plugins/props/index.js#L8–L23). That hook only runs the first time the class's constructor is called — i.e. on the first instance.

customElements.define(name, Class) reads Class.observedAttributes at registration time and caches the list. By that point, first_constructor_static has not yet run, so the static getter is undefined and the cached list is empty. Subsequent setAttribute() calls therefore never reach attributeChangedCallback.

The Props.initializeFor path manually walks observedAttributes on mount (after construction has run, so the getter is now in place), which is why pre-set attributes work.

Reproducible on

main and signals-props. Setup logic in first_constructor_static is byte-identical on both — this is pre-existing, not introduced by #91 / #97.

Suggested fix direction

Run the observedAttributes getter setup at class declaration time (e.g. via the setup static hook) instead of first_constructor_static. By the time customElements.define reads the list, the getter should already resolve to the right value.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions