What should `bind:selected` be when `maxSelect={1}`?
Length-1 array or the value? Currently gives a length-1 array.
This issue arose out of private discord communication with fnAki dated 21/06/2022. Copied here so I don't forget about it.
I decided to throw previous packages and use
svelte-multiselectfor my dashboard. Love it so far (the only multi-select package that ACTUALLY allows you to change its style). Few things however,
the single select acts as a multi select unfortunately, so I can't really bind it directly to my settings store and would need to do more work and listen to the change event (basically make my own component on top of MultiSelect) A: I think i know what you mean. bind:selected gives an array even if maxSelect={1}, is that it? i've found that annoying myself a few times. although it can also make it easier to know that bind:selected will always be an array. needs less safety checks and can avoid indexing errors. happy to take a PR to change it though if you think it not being an array is generally better
when you have required=true in a single select, the remove icon for the single item selected should not appear for the item selected, so that there can always be one required value A: Sometimes I find it annoying not to be able to empty a select input. But i see your point. We should maybe add a prop allowRemoveLast or sth so devs can control this behavior.
I'm not sure what the purpose of the loading state is? I don't think there's a way I can perhaps run an async function when the client tries to search? A: That's correct, it's purely a UI thing to display to the user something is currently happening in the background. you can do whatever you want with that, e.g.
let loading = false function search_handler() { loading = true // will cause MultiSelect to show a loading spinner to the user // do some work like fetching options to display to the user from a server loading = false // reset to normal state when finished } <MultiSelect on:enter={search_handler} bind:loading
2 todos from this:
- Add
allowRemoveLastprop - Decide whether
bind:selectedshould return a single value (not as a length-1 array) whenmaxSelect={1}.
+1 for this.
I need to handle single select like this at this moment:
import type { DispatchEvents } from 'svelte-multiselect'
const updateSelected = ( event: CustomEvent<DispatchEvents['change']> ): string => {
const { type, option } = event.detail
if (type === 'add') return option as string
else if (type === 'remove') return ''
}
<MultiSelect
required
maxSelect={1}
options={myOptionArray}
on:change={(event) => (myBindSelected = updateSelected(event))}
/>
@davipon Good to know there's interest.
For now, wouldn't this be a simpler workaround for your use case?
<script>
import MultiSelect from 'svelte-multiselect'
const myOptionArray = [`...`]
let selected = []
$: value = selected[0] ?? null
</script>
<pre><code>value = {value}</code></pre>
<MultiSelect options={myOptionArray} bind:selected maxSelect={1} />
@davipon Good to know there's interest.
For now, wouldn't this be a simpler workaround for your use case?
<script> import MultiSelect from 'svelte-multiselect' const myOptionArray = [`...`] let selected = [] $: value = selected[0] ?? null </script> <pre><code>value = {value}</code></pre> <MultiSelect options={myOptionArray} bind:selected maxSelect={1} />
Yes, this was my first attempt, but I changed to use my previous approach.
The reason is that I have a form that has multiple single-select and multi-select:
interface myForm {
singleSelect: string
multiSelect: string[]
}
We can't get selected values by submitting the form so far. I need to create an object like myForm to store them whenever there is a change.
<MultiSelect
name="singleSelect"
required
maxSelect={1}
options={myOptionArray1}
on:change={(event) => (myForm.singleSelect = updateSelected(event))}
/>
<MultiSelect
name="multiSelect"
required
options={myOptionArray2}
bind:selected={myForm.multiSelect}
/>
How about binding the selected values into a store?
import { writable } from 'svelte/store'
const formData = writable({})
and then in your form component
<script>
import MultiSelect from 'svelte-multiselect'
import { formData } from '../stores'
const myOptionArray = [`...`]
let selected = []
const fieldName = `mySingleSelect`
$: $formData[fieldName] = selected[0] ?? null
</script>
<MultiSelect options={myOptionArray} bind:selected maxSelect={1} />
How about binding the selected values into a store?
import { writable } from 'svelte/store' const formData = writable({})and then in your form component
<script> import MultiSelect from 'svelte-multiselect' import { formData } from '../stores' const myOptionArray = [`...`] let selected = [] const fieldName = `mySingleSelect` $: $formData[fieldName] = selected[0] ?? null </script> <MultiSelect options={myOptionArray} bind:selected maxSelect={1} />
And how would that work if I want to set values from a store?
$: selected = $formData.fieldName
I am currently trying to get multiple select components running on the same page but that doesn't seem to work.
@magicbyt3 If you want to set values from a store, it's best to bind directly to the store. In that case, the store value needs to be an array. There's currently no way around that if you want to keep the ability to update the component via the store.
See https://github.com/janosh/awesome-sveltekit/blob/47bad49c72/site/src/components/Filters.svelte#L26 for a real-world example.
@magicbyt3 If you want to set values from a store, it's best to bind directly to the store. In that case, the store value needs to be an array. There's currently no way around that if you want to keep the ability to update the component via the store.
See https://github.com/janosh/awesome-sveltekit/blob/47bad49c72/site/src/components/Filters.svelte#L26 for a real-world example.
Thanks for that. For my purpose it was better to create an own multi select component. Not ready to publish yet but works so far. Your implementation inspired me on the way. :-)