-
-
Notifications
You must be signed in to change notification settings - Fork 4.5k
Use actions on Svelte components #6771
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
Comments
Why don't you do this: <WithShortcut shortcut="Control+C" action={doThing} let:shortcutLabel>
<Button title={shortcutLabel} on:click={doThing}>Click me!</Button>
</WithShortcut> Sure, it's a tiny bit of duplication, but it works and is easier to understand than the back and forth spaghettiing. Remove duplication by re-exposing the function to the button: <WithShortcut shortcut="Control+C" action={doThing} let:shortcutLabel let:shortcutAction>
<Button title={shortcutLabel} on:click={shortcutAction}>Click me!</Button>
</WithShortcut> This also allows I think #3617 could also solve this in a different way, e.g. by passing the component back via |
The issue is that shortcut does not execute the function but rather it gets the element and simulates a click:
I guess I could also pass {disabled} to This is basically a perfect case for
|
Sounds a lot like manually and imperatively managing state and hoping it's consistent. Shouldn't this happen automatically, e.g. via some state in a store or something?
What would you need I might not see the bigger picture yet, but this seems way more complicated than it should be. |
In fact that's exactly what I am doing. Here's an actual example from our code: <WithShortcut {shortcut} let:createShortcut let:shortcutLabel>
<WithState
{key}
update={() => document.queryCommandState(key)}
let:state={active}
let:updateState
>
<IconButton
tooltip="{tooltip} ({shortcutLabel})"
{active}
{disabled}
on:click={(event) => {
document.execCommand(key);
updateState(event);
}}
on:mount={(event) => createShortcut(event.detail.button)}
>
<slot />
</IconButton>
</WithState>
</WithShortcut>
But I don't want to inline the button, because I want to bundle the HTML with styling and functionality. That's why I'm using |
To me this does not look like Svelte at all, but I guess people do things a lot different than I do (we had this discussion in #6255 already, I'd never use a context that way). From the top of my head I would:
I know that this has nothing to do with your feature request, but most feature requests I see can be solved by doing things differently. In my experience Svelte components can be very lightweight and independent if abstractions happen at the right place. I've never once had the need to use sth. like I will refrain from hijacking your feature request any further, maybe this was helpful though. |
Thanks a lot for your comments, @Prinzhorn, I think I might just have gotten a few good ideas out of this. However, I am still standing by this idea. This might have been a bad example to argue for it, so here's another go (actually my original motivation, but I thought it was too akward to explain): We want our Svelte app to be extensible. Doing so is quite the challenge, and having Here's an example. I have a simple (This would also require that you can define inline |
I think this sounds more like mixins than actions 🤔, not sure what others are doing in this space (you can get pretty far with
Now this is getting too complex for discussing this here and these type of architectural decisions require more in depth understanding than what I can provide here. I will say this: I personally would think of the extensions in terms of data, not in terms of UI elements. My UIs are data/store driven. The UI is just a way to visualize the data. Your data could flow through all of of the extensions and the extensions can make decisions (e.g. setting |
I always thought of actions as mixins. Even the actions used in the tutorial could be aptly described as
Yep, we experimented with this, but what we found is that you loose most of Svelte's niceness like slots, and instead you'll pass around deeply nested objects. |
Guess I'm late to the party, but I have another use case. I made an action that can be applied to input and textarea elements, and listens for input events on them. The action basically just starts a setTimeout when that happens, and when the timer fires, a method that the user submitted when making the action would be called. A very simplified demonstration of what it basically does:
Now, I may want to use this on components that are wrapper- or custom built components that mimics native ones (Button wrappers, custom SVG checkboxes, or whatever). So how would my action know what to do with any given Component that I might use it on? In general it can't know what to do - but what it could do, is say that the action requires these and these behaviors of the Component. Maybe for the action to make sense to use on a component, it requires that the Component dispatches custom input events (with the data set to whatever data makes sense - could be a boolean in the case of a checkbox, or a Date in the case of a date picker). I don't think it's any more work for the author of the action to define the interface for Components, than it is to determine the type of and handle support for all the various kinds of native elements that the action could also be applied to. I envision just passing the component (the object you get when you bind:this to a component) as the first parameter to the action, instead of the dom element (which is the case for native DOM elements). But that's just an idea, of course. |
Describe the problem
There's some things I often find myself doing:
E.g. I have one component
WithShortcut
, which exports alet:
binding to create a shortcut, e.g. like this:That's quite the boilerplate, imo, and it leads to code which doesn't feel good, as we're using events, just to be able to listen to the lifecycle event outside of the component. Also, using
on:
feels more correct, if you're reacting to events, but what we're doing here is extending the component, for whichuse:
would feel better.There's other issues here. The cleanup for the button happens in
Button
, but the cleanup code for the shortcut happens inWithShortcut
.Describe the proposed solution
What would be much nicer imo, is if we could use
use:
actions on components.The semantics would be similar to HTML elements: the action's
mount
corresponds toonMount
, anddestroy
corresponds toonDestroy
.This means that the whole thing is basically just syntax sugar. I've built an example here, where I'm recreating a Component
use:
action with props:UseAction.svelte REPL
I'll also copy it over here.
<MyComponent use:action={param} />
translates to:The above code could then be rewritten as (I know
{@const
is not a thing yet, but it's also a nice feature):See here for a REPL of how I wish I could do it.
I believe this makes it more semantic.
This would also be easy to teach new users, as it mirrors how they behave on elements:
(This feature seems quite natural to me, and I was surprised to have not found this feature suggestion. If it has been suggested before, I'd appreciate a link.)
Alternatives considered
As I mentioned above, I currently use
onMount(() => dispatch("mount", { button }));
. See here for a REPL.One might say, I could import
registerShortcut
fromButton.svelte
and use it there directly, but I don't want to teach every component that might get a shortcut how to create them, and eventually how to assign tooltips (title
).Importance
would make my life easier
The text was updated successfully, but these errors were encountered: