-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Allow hyphenated properties to be defined in a component #3852
Comments
A component's 'unknown prop' warning is supposed to be suppressed if the component uses |
Thanks for the response. I believe that I was still getting it, but I'll go back and post a snippet when I get back to a computer. |
In this specific case, the property looks like HTML5 user-defined data. Maybe take hyphenated names and make the second word of the hyphen a key inside the first name? Such as:
|
True, but I am not sure that would be the only use case. |
@dkondrad @ahopkins yeah I think this is a bad example. Ignoring
where I'd really prefer to declare it as:
|
@antony I agree. It just so happened that my example I was using something that required a I agree with your example. |
@ahopkins Did you ever get a reproduction for the 'unknown prop' warning persisting despite the component referencing |
Of course I cannot recreate it now ...
As stated, as soon as |
@Conduitry I am not sure that this was ready to be closed. The warning was sort of a side matter. I think the issue (as displayed by @antony) is still a valid feature request. Unless you are saying this is not something that you would entertain? |
Yeah, fair, we can re-open this. |
This needs to be done before Rich comes along and does this: <Profile avatar_type="xxx"> But seriously... not sure what the behaviour should be if we did this: <Profile avatarType="xxx" avatar-type="xxx"> export let avatarType I suppose we could consider Another weird edge case: <Profile avatar--type="xxx"> I'd suggest that the above is left intact, thus ending up as the (un-addressable) prop |
Not knowing the feasibility of this... I like that suggestion. |
And what about when it's compiling to a custom element? When adopting Svelte, I tried to keep the hyphenated attribute names I'd previously had when writing CEs from scratch and couldn't figure it out in the time I had, so gave up. Do web components get access to |
@morewry It looks like they don't. I just tried it. I'd really like to see this feature implemented. Without it, it seems the only option is to pass in attributes as snake_case, which is inconsistent with the rest of the DOM elements. I can't even seem to get camelCase to work. |
The solution was already outlined by Mr @Rich-Harris in #875, just make the camelCase to kebab-case translation possibility. |
We are making transition from using polymer web components and upgrading our existing component library to use svelte web components where applicable. Inability to define hyphenated props is a blocker for us. |
@osamamaruf I've faced this problem too. import MyCustomComponent from './MyCustomComponent.svelte';
class MyCustomComponentWrapper extends MyCustomComponent {
static get observedAttributes() {
return (super.observedAttributes || []).map(attr => attr.replace(/([a-zA-Z])(?=[A-Z])/g, '$1-').toLowerCase());
}
attributeChangedCallback(attrName, oldValue, newValue) {
attrName = attrName.replace(/-([a-z])/g, (_, up) => up.toUpperCase());
super.attributeChangedCallback(attrName, oldValue, newValue);
}
}
customElements.define('my-custom-component', MyCustomComponentWrapper); MyCustomComponent.svelte <script>
export let someDashProperty;
</script>
<svelte:options tag={null} />
{someDashProperty} Then you can use it in this way: <my-custom-component some-dash-property="hello"></my-custom-component> Hope it helps until fix is coming |
Just wanted to say +1 for automatically converting I've been looking around for some "best practices" for publishing custom elements, and besides this article, I've also looked at the pattern used by the same author's chessboard-element, and it considers lowercase-kebab attributes to be equivalent to camel-cased props. @oranmor's temporary solution works great in the meantime, though! |
@oranmor do you have a "full" example? I don't understand how you implement the |
@benkeil in fact it is a full example. |
Here's one possible workaround for accessing hyphenated attributes sent into a Svelte wc. Have a
Obviously that won't be reactive. But it might be good enough depending on use case. The tick() call is necessary because of this issue: #2227 |
This is problematic for some SVG elements which have camelcased attributes' name. |
Hi, I tweaked @oranmor awesome workaround a bit to inject a wrapper during build-time, leaving the original Svelte component untouched, and no corresponded wrapper component file needed. (Very useful if we have multiple WCs) First, I create a /* src/utils/custom-element.js */
export const customElements = {
define: (tagName, CustomElement) => {
class CustomElementWrapper extends CustomElement {
static get observedAttributes() {
return (super.observedAttributes || []).map((attr) =>
attr.replace(/([a-zA-Z])(?=[A-Z])/g, '$1-').toLowerCase(),
);
}
attributeChangedCallback(attrName, oldValue, newValue) {
super.attributeChangedCallback(
attrName.replace(/-([a-z])/g, (_, up) => up.toUpperCase()),
oldValue,
newValue === '' ? true : newValue, // [Tweaked] Value of omitted value attribute will be true
);
}
}
window.customElements.define(tagName, CustomElementWrapper); // <--- Call the actual customElements.define with our wrapper
},
}; Then I used esbuild /* esbuild.js */
import { build } from 'esbuild';
import esbuildSvelte from 'esbuild-svelte';
import sveltePreprocess from 'svelte-preprocess';
// ...
build({
entryPoints,
ourdir,
bundle: true,
inject: ['src/utils/custom-element.js'], // <--- Inject our custom elements mock
plugins: [
esbuildSvelte({
preprocess: [sveltePreprocess()],
compileOptions: { customElement: true },
}),
],
})
// ... When build, my original Svelte components like this: <!-- src/components/navbar/navbar.wc.svelte -->
<svelte:options tag="elect-navbar" />
<!-- Svelte Component ... --> Will have an output like this: // components/navbar.js
(() => {
// src/utils/custom-element.js
var customElements = {
define: (tagName, CustomElement) => {
// Our mocked customElements.define logic ...
}
};
// Svelte compiled code ...
customElements.define("elect-navbar", Navbar_wc); // <--- This code compiled by Svelte will called our mocked function instead of actual customElements.define
var navbar_wc_default = Navbar_wc;
})(); This idea should be adaptable with other bundlers as well. I want to share this since it's might be useful, or it might be a bad solution. So, please feel free to leave feedback. Here is the Svelte WC collection project I'm working on using the above technique (+ WindiCSS and Storybook). |
2 years and the workaround is still the best solution? 😕 |
It should also be noted that the workaround is for "custom elements" (=web components). A "custom element" is not the same thing as a Svelte component. I for one do not really care about web components, but would like to have all Svelte components support hyphenation in properties and component names. |
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 |
If not using the Vite plugin, you can also watch $$props on the child component. This does work, and you can reassign the $$props properties to internal component variables. let myValue = null
$: {
console.log($$props)
myValue = $$props['my-value']
} |
For me it's not about warnings; I've been trying to avoid using |
Based on the current documentation, if you are defining a custom element, I would think that you're able to do this now by specifying an <svelte:options customElement={{
tag: "my-tag",
props: {
myValue: { reflect: true, attribute: "my-value" }
}
}} />
<script>
export let myValue = "";
</script> I haven't actually tried this, and it obviously isn't an automatic solution for all properties matching up with all attributes, but it's at least some kind of proper solution to the problem with a direct binding, assuming you are using Svelte for custom elements. Still, an idiomatic and automatic solution that works with both custom elements and traditional Svelte components would be ideal. Perhaps there could be a <svelte:options attributes: (propName) => attributeName />
<svelte:options attributes: "kebab" /> Then, if a value for |
How do I use the If I have this in Thingy.svelte:
and then invoke it with
TypeScript will complain that |
I'm assuming value of $: myValue = $$props['my-value'] as SomeType Otherwise: $: myValue = $$props['my-value'] as unknown as SomeType |
@Caellian Unfortunately I can't test this in the REPL as sveltejs/svelte.dev#853 still hasn't been implemented — but isn't that going to set the type of myValue within Thingy.svelte, while still leaving the error on the invocation of I can't see how that cast will affect the signature of |
Declaring hyphenated attributes will be possible in Svelte 5 using the |
Is your feature request related to a problem? Please describe.
Given the usage of the following hypothetical component:
I receive a warning that
data-tooltip
has not been declared as a property on theIcon
component. However, there is no way to do that since it is hyphenated.Describe the solution you'd like
Inside my component, it would be nice if I could declare the property like this:
Describe alternatives you've considered
I know that the properties are also available in
$$props
. But that doesn't really solve the problem.While this would give me access to the value, it doesn't remove the in browser warning.
How important is this feature to you?
It's annoying. Important? Well, certainly not a deal killer. And for now I just am using the form:
<Icon tooltip="foobar" />
. And then I am passing the property to the hyphenated version inside the component.The reason this came about was I was trying to just use a spread operator and pass props thru. But, here I cannot because I don't want to keep seeing those warnings all over the place.
The text was updated successfully, but these errors were encountered: