-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Passing data to custom elements #875
Comments
@evs-chris pointed out that we can see what props the custom element is expecting at runtime. We could potentially do that, and fall back to setting attributes if the prop doesn't exist. |
I'm trying to pass attribute in my <custom-element name="test"></custom-element> and I'm getting
is this not supported yet or I don't have a correct setup? I'm using Svelte component looks like this <h1>Hello {{ name }}!</h1>
<script>
export default {
tag: 'custom-element'
}
</script> sorry for hijacking this issue. |
@Rich-Harris even if messy, your solution seems a good compromise to get immediate interoperability with custom elements attributes and properties. In this regard, it would be nice if soon or later we could add Svelte to Custom Elements Everywhere with the ultimate goal to pass all the tests 🤓 |
@zigomir Have you found a solution to your problem? I'm getting the same error message |
@eddyloewen nop. |
@zigomir @eddyloewen I'm having the same problem. When I use svelte to compile custom elements, the built file is in this order: function create_main_fragment () {...}
class MyElement extends HTMLElement {
...
// set method isn't defined here, but mixed in below
attributeChangedCallback(attr, oldValue, newValue) {
this.set({ [attr]: newValue });
}
}
customElements.define("my-element", MyElement);
assign(MyElement.prototype, {
...
set: set,
_set: _set
...
}, {...}
});
...
function set () {...}
...
function _set() {...}
...
export default MyElement; If I move the call to define the custom element down below where the set method is mixed in with the prototype, I no longer get the error. function create_main_fragment () {...}
class MyElement extends HTMLElement {
...
// set method isn't defined here, but mixed in below
attributeChangedCallback(attr, oldValue, newValue) {
this.set({ [attr]: newValue });
}
}
// previously here
assign(MyElement.prototype, {
...
set: set,
_set: _set
...
}, {...}
});
...
function set () {...}
...
function _set() {...}
...
// now here
customElements.define("my-element", MyElement);
export default MyElement; I'm a bit surprised by this, even though I can outline a series of events that might lead to it (sections 2.4-2.6 of https://www.w3.org/TR/custom-elements/#custom-elements-api). Still, I wouldn't expect the The way I'd expect this to process, by metaphor, is like this:
This would go "b a", not "a b". So I have to be wrong about something, I'm just not sure which part. Still, as a temporary palliative, you might be unblocked by hacking this reorder in. |
Also, @Rich-Harris I like your suggestions. FYI AFAIK Polymer and Skate both allow reflection to the attribute via a prop config, but don't do so by default. skatejs/skatejs#838 https://developers.google.com/web/fundamentals/web-components/customelements#reflectattr |
The decision to mirror between properties and attributes really lies with the custom element author, not the framework. Svelte shouldn't be concerned with whether the binding is to attributes or properties—this digs too deep into the implementation and cares too much about the internal workings of a given custom element. The css example you cited is not something Svelte needs to know or care about, but rather something the custom element author, and potentially consumer, should know and care about.
The custom element can be designed to handle this robustly by using mirroring rather than expecting frameworks everywhere to behave uniformly. Alternatively, is it really so wrong to have a custom element that uses attributes and props independently (i.e. attributes for the purpose of css only)? The question then shifts from: What should Svelte do exclusively and automatically? to Should Svelte provide independent binding options for properties and attributes? If the answer to the later question is yes, then you return binding control to the consumer—the only decision maker with enough information to truly know whether you should bind to an attribute or a property. Consumers are then free to bind as each custom element requires... and for robust elements it won't matter. But either way, this isn't really Svelte's concern. |
Thanks for the feedback everyone. I finally got round to working on this — see #1636. @alindsay55661 On reflection I think you're right that it should be down to custom elements to reflect props back to attributes. I think the best approach is to use props if @zigomir @eddyloewen @CH-RhyMoore apologies for missing those bug reports earlier. What's the simplest complete reproduction? There's a test that seems like it should cover that scenario, but perhaps I'm missing something? Could you open a separate issue if so please? Thanks |
@Rich-Harris I'm trying out this right now (with Svelte computed: {
method({ attr1, attr2}) {
console.log(attr1, attr2)
}
}
when you use custom element in your html as: <custom-element attr1="foo" attr2="bar"/> is this intended? Another unfortunate side effect that I'm seeing is that when I use |
Ah good, it must be fixed then.
I would use the word 'expected' rather than 'intended'... this is an unfortunate thing about custom elements. It would be easy enough to generate code that batched up property changes... class SvelteComponent extends HTMLElement {
get foo() {
return this.get().foo;
},
set foo(foo) {
if (!this._pendingChanges) {
const pending = this._pendingChanges = {};
Promise.resolve().then(() => {
this._pendingChanges = null;
this.set(pending);
});
}
this._pendingChanges.foo = foo;
},
// ...repeat for each property
} ...but it could easily lead to incorrect behaviour if setting properties is supposed to have some immediate effect: element.foo = 'potato';
console.log(element.offsetWidth); // incorrect, because state change is pending You'd have to get super-sophisticated to make sure that reading properties like
This is because it uses Shadow DOM — if you look at the generated CSS you'll see that it's creating a |
👍 I see, thanks for the response! Another question; is there a way to tell through svelte that custom property attribute is a number and not a string? <custom-element start="1" /> Svelte will generate a getter as get start() {
return this.get().start;
} but this will always return a string. I know I can do const el = document.querySelector('custom-element')
el.start = 1 but that just doesn't look so good :) |
In raw HTML? I don't think so, no. In a Svelte component you could do this, and it should work: <custom-element start={1} /> |
use props when passing data to custom elements (#875)
Released 2.9.11 with better custom element handling. We now pass all the tests on custom-elements-everywhere; I've raised a PR with them webcomponents/custom-elements-everywhere#257 |
Not sure if this was resolved a different way, but I think there should be a way to pass data as properties, similar to the way Vue handles it. I believe this would make Svelte & Vue web components more interoperable.
|
Why is this closed? This is not solved yet: kebab-case vs camelCase. Let's say I want attribute "share-url" for my web component, I can't see a way to do it. This won't work: I'd argue this is not solved by any means yet. |
Is it possible that exporting an object from the svelte component might be a better fix? |
Please re-open, don't work with camelCase if attr is kebabCase: HTML: <my-component header-text="hello"></my-component> Svelte file: <svelte:options tag="my-component" />
<script>
export let headerText;
</script>
<div>
{headerText}
</div>
<style lang="scss">
</style>
Workaround:You can use |
For those who needs this so bad, I just created a vite plugin that adds kebab-case support for Svelte components vite-plugin-svelte-kebab-props |
When passing data to a custom element, there are two possibilities — properties, or attributes.
Right now, Svelte doesn't have any understanding of custom elements, so on encountering something like this...
...it will look here, fail to find a property corresponding to the
foo
attribute, and fall back to writing this:That's sub-optimal, because it means we can't pass down non-string values — especially not objects and arrays. For those, we need to use props.
At the same time, always using props could be a problem for custom elements that expect attributes to change. It's also a problem if a custom element has CSS like this, say...
...because then if you have a situation like this...
...the prop will be maintained correctly but the attribute won't ever change, meaning the styles will never apply.
A possible solution
Do both. Or rather, always set a prop, but also set an attribute if the value is primitive.
So the example at the top would become this:
For dynamic values, it would be more like this:
Needless to say, all that logic would live in a helper.
kebab-case vs camelCase
Since props can't be kebab-case, I propose that we translate kebab to camel when setting props:
This might seem slightly messy, but I think it's the most pragmatic way to deal with this stuff, and the way that will result in the least surprising behaviour for the largest number of people. No-one ever said custom elements were particularly well designed. (Actually, lots of people did, but most of them work for Google.)
Thoughts?
The text was updated successfully, but these errors were encountered: