Skip to content

Custom element properties cannot be overridden #13706

@mattheww-skyward

Description

@mattheww-skyward

Vue version

v3.5.18

Link to minimal reproduction

https://stackblitz.com/edit/vitejs-vite-nhmrulst?file=src%2Fmain.js

Steps to reproduce

I'm creating a custom datepicker control. Similar to the native <input type="datepicker"> control, I want to reject assignments to the value property that aren't valid dates.
An easy solution would be to subclass the custom element:

const baseCustomElement = defineCustomElement({
  props: { myProp: Date },
  setup(props) { ... },
});
class DerivedCustomElement extends baseCustomElement {
  set myProp(newValue) { if (isValid(newValue)) { super.myProp = newValue; } }
}

However, this doesn't work:

customElements.define("my-elem", DerivedCustomElement);
const elem = document.createElement("my-elem");
document.body.append(elem);
elem.myProp = "invalid value";
console.log(elem.myProp); // returns "invalid value"

What is expected?

DerivedCustomElement.myProp's setter should be called, and should not pass the new value into the component.

What is actually happening?

The setter is never called, because it is overridden when the component is mounted. This is done in apiCustomElement:_resolveProps. The comment, "// defining getter/setters on prototype", makes me think this is a bug.

An alternative solution for my use case would be to enforce prop validation in apiCustomElement, and not call _setProp if validation fails. However, this would have wider effects and would probably need more analysis, an option to toggle it off, and so on.

System Info

Any additional comments?

My proposed fix is to define the properties on the prototype, for real. In addition to making them overridable, this would probably also result in a small speedup, as they wouldn't have to be defined every time an instance is created and mounted.
I'd be happy to make a pull request for this, if accepted.

Metadata

Metadata

Assignees

No one assigned

    Labels

    🔨 p3-minor-bugPriority 3: this fixes a bug, but is an edge case that only affects very specific usage.scope: custom elements

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions