-
-
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
Input modifiers #3937
Comments
Might it be nice to add |
Would be good for |
I still would prefer with the reasoning being: "Don't make me learn how to use another framework" It would just be easier to use the built tools then to write redundant wrappers around something that already exist and can do it for you |
I also like |
Is there a workaround for now? |
|
@antony Thanks for the answer, but I needed 2 way binding. |
Just an idea: Maybe this concept could work in a more general way: I'd imagine having a way to change the internal representation of an input would be very nice. This isn't very well thought out yet, but what about something like
The nice thing about this would be that you could also use this with other kinds of inputs well. For example, you could automatically have a localized input like this with less code, for example:
...and you would not need the temporary internal variable any more. I personally find my own code suggestion a bit ugly here, but would love the general idea of being able to have a variable contain something different than the exact input value given by HTML. |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
Is there any update on this issue? I have run into this problem with |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
Thought about the same recently, too, but in a more extendable way: what if the modifier is an object you can provide which has two methods transforming the value on the way in/out? |
Similar to what dummdidumm proposed, I liked the value converter approach Aurelia used for this problem.
|
I think this is way to specific and might be missing the big picture, as I've outlined here #7265 (comment) . The two comments above mine don't go far enough to solve a greater problem. What I imagine is some sort of Proxy but for reactivity. Stores give us that level of control (hooking into declarative assignments with imperative code), regular reactivity does not. In the most verbose way this would look like this: <script>
import proxy from 'svelte/proxy';
import { format } from 'date-fns';
let value = new Date();
$: console.log(value);
// The proxy specification.
const dateTimeAsString = {
get(d) {
return format(d, "yyyy-MM-dd'T'HH:mm");
},
set(s) {
return new Date(s);
},
};
// This reactively marries `value` and `proxiedValue`.
// Everytime `proxiedValue` is written, it is syncted with `value` through `set`.
// Everytime `value` is written, it is syncted with `proxiedValue` through `get`.
// Both `value` and `proxiedValue` can be used just like any variable.
// This gives us the $store declarative magic for any variable.
let proxiedValue = proxy(value, dateTimeAsString);
</script>
<input type="datetime-local" bind:value="{proxiedValue}" /> However, the pipe shorthand allows using a proxy implicitly: <script>
import { format } from 'date-fns';
let value = new Date();
$: console.log(value);
const dateTimeAsString = {
get(d) {
return format(d, "yyyy-MM-dd'T'HH:mm");
},
set(s) {
return new Date(s);
},
};
</script>
<input type="datetime-local" bind:value|dateTimeAsString /> And of course Svelte comes with a set of build-in proxies: <script>
import { dateTimeAsString } from 'svelte/proxy';
let value = new Date();
$: console.log(value);
</script>
<input type="datetime-local" bind:value|dateTimeAsString /> Edit: a few words on what I mean by "big picture": This is not limited to I also want to point out that my example above makes it look like I'm sure there is a solution to #4933 and related problems in there as well. |
I was playing around with store transforms to achieve a similar result (you can see here: https://svelte.dev/repl/8d60f27d3183493d8ee2264f535167f4?version=3.48.0). Something I noticed comes up which may not be immediately obvious, is that not all transformations are symmetrical. That is, not every mapping of A to B has a mapping from B to A. Sometimes a mapping may be ambiguous and sometimes there may just be no mapping. If the suggestion is to have a fixed list of "out of the box" conversions than this issue may be avoidable, but if the list of conversions is to be extensible it would be worth discussing. For the library I wrote, I chose to have the read and write transforms offer two forms: I'm very interested to hear these conversations so I can inform the design choices in my libraries: npmjs @crikey/* |
I created #9998 for |
@cupcakearmy, your workaround https://svelte.dev/repl/dc963bbead384b69aad17824149d6d27?version=4 contains an infinite effect trigger loop bug (stopped before the 2nd iteration of an effect) that still makes one effect to trigger the other.
|
2-Way date binding | valid dates only svelte v4 |
It's theoretically fixable by using stores. |
@cloudymeatball
but still has
And add some UX bugs when I tried to input some random values in the date input |
@cloudymeatball for some reason, on the latest windows and chrome, pressing 4 consecutive digits while having my cursor selection on the input year still only take the last digit in account (except 0, that simply doesn't work), so I can only set 1901-1909 |
@Malix-off See this breaking https://svelte.dev/repl/f9441e746728408d8ed481e2d3572896?version=4.2.8 which uses the web Date object instead of a third-party library. This fixes the year issue. |
It's pretty close, yep! |
Never mind, it does the same with the native |
@cloudymeatball my bad! |
@Malix-off thanks for the kind words.
|
I am currently trying to build a rune port |
@Malix-off Hi it's me, hacky. https://svelte.dev/repl/68648cb51cf44fe18686468dc2b64d81?version=4.2.8 I believe I first saw this when I was trying to learn Vue, but i might have seen it in svelte as well. Same as ^ but a throttling version https://svelte.dev/repl/c64696445fc841958b901ef3972a7827?version=4.2.8
|
I am working on a rune port. Unfortunately, in Svelte 5 with Runes, it seems that circular effects doesn't stop after the first effect of the second cycle like it seemed to do in Svelte 4, and produce an infinite chain reaction that triggers this error:
Thus, as in my I opened a new help thread in the Discord , if someone want to help me debug that. Update
|
WorkaroundsI plan on making a workaround for every input types that features other IDL attributes than I am also willing to publish them as a component library dependency, but idk how currently
|
I'm not sure we need input modifiers nor a |
@dummdidumm how would this work without |
You're right, that's inconsistent. Either we disallow writing to properties of
What do you mean by that? |
Just realized that you don't even need |
|
I've been trying to bind an No permutation of proxies and effects I've tried worked with <input type="date" value={value?.toISOString?.().split("T")[0]}
on:change={({target: {value: v}}) => value = v ? new Date(v) : undefined} /> Works perfectly in svelte 4 style and with runes |
Removing the Timezone will result in bugs, seen it many times xD |
Also, this is really ugly code imo |
yep, ugly af, but without a framework level solution, this is seemingly the only way. Shared this only in case there's more peeps like me who struggled and need something that works now |
Totally, just wanted to say I'm not satisfied with that as the final solution to this problem. So we need to come up with something better. But thanks for sharing the workaround ❤️ |
Handling arrays of objects has become quite complex. For instance, I have an array of objects where each object has a date field. I need to validate this array using a schema validator, and it's quite challenging to do so efficiently. It would be incredibly helpful if Svelte had binding modifiers to simplify this process. |
That's not what I'm experiencing, unfortunately. Quite the contrary. 😕 I frequently find myself reaching for binding modifiers (being accustomed to them from other declarative frameworks, such as @Binding, SwiftUI's equivalent for The problem with store/rune-based "workarounds" is that they attempt to tackle the issue at the wrong level. Stores have to be declared at the (outer) component's level and runes (afaik) can't just be created on-the-fly either. The the binding however is all the way down at the inner component's level, which in case of components with collections and corresponding As a result of this any such store/rune-based "workaround" as outlined above will —afaict— inevitably fall short in situations where the outer component's state is non-trivially structured, requires binding modifiers for nested state fields (especially if the structure is not fixed) and the component needs to bind its state to a collection of sub-components (e.g. via @hasinoorit already mentioned arrays, but it gets much, much worse when dealing with deeply structured models, let alone dynamic or polymorphic ones. And even if stores/runes are applicable to your particular scenario: what if your component needed to provide multiple different representations of the same bound value? Imagine a component for a numerical value that provides control through both, a slider, as well as a stepper input, while also showing a formatted textual representation with unit in a label. Now you either have to add multiple competing projection stores, or merge them into a single drastically more complex store. Now for a single Either way you're adding complexity and noise to your component that shouldn't have to be there in the first place. After all you often just needed this one field's value deep down in your component's store nested structure to be passed to A binding's modifier should not require external state in order be able to apply a (pure) projection to its value. If Svelte had proper first-class binding modifiers (as outlined above by @Prinzhorn), then such issues would simply vanish: Given a model like this: const model = {
foo: { bar: { value: number }},
}; … and the need to bind const numberAsArray = {
get(value) { return [value]; },
set(values) { return values[0]; },
}; … use it: <Slider bind:value|numberAsArray={model.foo.bar.value}> … and move on, without spending a minute on the matter (as it arguably should be). With proper stateless binding modifiers you don't have to touch the outer component's props/state/stores, nor do you have to make any changes to the inner component. Composition at its best. 💪🏻 And in 80% of use cases (such as |
Bindings would be a pretty good first step in that direction though (assuming their design is implemented as a restricted instance of the more general concept of reactivity proxies, thus allowing for progressive expansion later on). A comparatively small step (as compared to generalized reactive proxies), but with potential to bring massive benefits to Svelte by itself alone. With a few minor modifications the modifiers as roughly outlined by you in: interface BindingModifier<V, P> = {
get(value: V): P;
set(projectedValue: P): V;
}; … they could be made quite a bit more flexible, even: ParameterizationFor default modifiers to be flexible enough one might however want to consider allowing modifiers to accept parameters (with reasonable defaults, where applicable), such as accepting custom date formats. For this to work one would have to make Pseudocode<script>
const dateTimeAsString: (format?: string) => BindingModifier<Date, string> = {
return {
get(d: Date) {
return format(d, format ?? "yyyy-MM-dd'T'HH:mm");
},
set(s: string) {
return new Date(s);
},
};
};
</script>
<!-- Reasonable defaults: -->
<input type="datetime-local" bind:value|dateTimeAsString />
<!-- Easy customization: -->
<input type="datetime-local" bind:value|dateTimeAsString("yyyy-MM-dd") /> ChainingFor composing specialized from a chain of simpler and more general modifiers. Pseudocode<script>
type DateComponents = { year: number, monthIndex: number, day: number };
let value: DateComponents = // ...
const componentsAsDate: BindingModifier<DateComponents, Date> = {
get(c: DateComponents) {
return new Date(c.year, c.month, c.day);
},
set(d: Date) {
return { year: d.getFullYear(), month: d.getMonth(), day: d.getDay() };
},
};
</script>
<!-- Ad-hoc modifier composition: -->
<input type="month" bind:value|componentsAsDate|dateAsString("yyyy-MM-dd") /> LensingFor losslessly(!) binding against a composite value's partial projection, such as binding a date to an input that is only supposed to know about the date's year component. (For this to work the Pseudocode<script>
interface BindingModifier<V, P> = {
get(value: V): P;
set(projectedValue: P): V;
set(projectedValue: P, currentValue: V): V;
};
type Year = number;
let value: Date = // ...
const dateAsYear: BindingModifier<Date, Year> = {
get(date: Date) {
return date.getYear();
},
set(projectedYear: Year, currentDate: Date) {
currentDate.setFullYear(projectedYear);
return currentDate;
},
};
</script>
<input type="year" bind:value|dateAsYear /> |
#3527 (comment)
Is your feature request related to a problem? Please describe.
Date inputs have string bindings, which are rarely helpful.
Describe the solution you'd like
It should be possible to specify the type of binding:
In v4, we can switch the default over to the
value|date
behaviour.Describe alternatives you've considered
Adding
valueAsNumber
andvalueAsDate
bindings. There are a couple of reasons not to do this:number
andrange
inputs are currently handledvalueAsNumber
instead of justvalue
. It's unergonomicHow important is this feature to you?
Medium
The text was updated successfully, but these errors were encountered: