Skip to content
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

bind:group does not work with nested components #2308

Open
imbolc opened this issue Mar 25, 2019 · 27 comments · May be fixed by #11256
Open

bind:group does not work with nested components #2308

imbolc opened this issue Mar 25, 2019 · 27 comments · May be fixed by #11256
Milestone

Comments

@imbolc
Copy link

imbolc commented Mar 25, 2019

I'm trying to bind a store variable to group of checkboxes and it works till I move checkbox into a separate component, after that only one checkbox can be chosen at a time, here's an example from repl: https://gist.github.com/imbolc/e29205d6901d135c8c1bd8c3eec26d67

@Rich-Harris Rich-Harris added this to the 3.x milestone Mar 26, 2019
@Rich-Harris
Copy link
Member

Yeah, this won't work as things currently stand — the group has to be within the component.

I suppose we could have a global Map of binding groups, though I'm not sure what the change notification looks like (for components other than the one with the element from which a change originated). This could be something to ponder post-3.0.

@bestguy
Copy link

bestguy commented Oct 6, 2019

Is there a workaround for this in v3? It makes components that use a checkbox pretty difficult/confusing to use if you need to use groups.

https://svelte.dev/repl/1565708677134e418e256234984d90ef?version=3.12.1

@NikolayMakhonin
Copy link

Solution of this problem but without keep order of the selected items:

https://svelte.dev/repl/de117399559f4e7e9e14e2fc9ab243cc?version=3.12.1

@NikolayMakhonin
Copy link

@boleeluchshiy
Copy link

boleeluchshiy commented Nov 1, 2019

I know, this is obvious, but still. For those, who run into the same problem: It would be sufficient to make separate component for the group of checkboxes, e.g. CheckboxGroup, which you can bind to a group <CheckboxGroup { checkboxes } bind:group/>.

See the example and learn some russian words :) https://svelte.dev/repl/faabda4cabd544bd858a8a8abd0095f5?version=3.12.1

@kevmodrome
Copy link
Contributor

This came up in the Discord today, what would be needed to make this work? Intuitively I feel like this should work out of the box.

@tamasPetki
Copy link

@didier
Copy link

didier commented Apr 22, 2021

Is this still being looked at? Would love for this to "just work", much like the rest of Svelte 😄. If there's anything I can do to help, let me know.

@arggh
Copy link
Contributor

arggh commented Apr 27, 2021

What if you try like this?:

@tamasPetki if you switch the inputs' type to "checkbox", you can only select one item in your example.

https://svelte.dev/repl/45496f841fef41cc91012b12abf3f3fa?version=3.20.1

@SystemDZ
Copy link

@didier
Copy link

didier commented Apr 29, 2021

@SystemDZ Your example doesn't use nested components. The issue only occurs when nesting a component and trying to bind them to an input group.

@autumnloveless
Copy link

is there any update to this? Trying to figure this out myself.

@Masstronaut
Copy link

Just encountered this issue. I'm fine with the workarounds above, but I'm less thrilled with spending several hours figuring out I didn't have a bug all along - it was in svelte. I'm sure plenty of other people will also spend several hours banging their heads against the wall trying to figure out what they are doing wrong.

@7antra
Copy link

7antra commented Jul 12, 2021

Absurd & hacky but works : https://svelte.dev/repl/02d60142a1cc470bb43e0cfddaba4af1?version=3.38.3

EDIT : @locriacyber found a better solution here : #2308 (comment)

@didier
Copy link

didier commented Jul 12, 2021

Absurd & hacky but works : https://svelte.dev/repl/02d60142a1cc470bb43e0cfddaba4af1?version=3.38.3

@7antra Hahaha, that's beautiful. Thanks for sharing.

@silvestrevivo
Copy link

@7antra you saved my day!!! thanks a lot!!!

@Abibibi
Copy link

Abibibi commented Sep 25, 2021

@7antra Yann!! Thank you so much for your help, coworker ;)

@joshuawjulian
Copy link

Is there any movement forward with this? I spent hours to figure out nested binds are the problem and not me.

@iacore
Copy link

iacore commented Jan 5, 2022

Here's a simple solution without bind:group:
https://svelte.dev/repl/6a17f4105e2b4bcd8d2df3eaff5bce0d?version=3.38.3

Here's a more complicated solution by passing one store to each checkbox component:
https://svelte.dev/repl/c8f4a50b563a4ebd9c9286aba98d4806?version=3.38.3

It is impossible without cross-component static analysis.
It is possible only when the input (checkbox) only set or unset its own element, without deleting existing choices in a list. For example, unchecking a checkbox with value "foo" will only remove "foo" from the bind:group array, without touching other components. This is the solution in #2308 (comment), and I think it should be the default semantic of bind:group for checkboxes.

Demo:
https://svelte.dev/repl/eeed24ad43cf4b01a53b75c5889afca6?version=3.44.3

abirtley added a commit to abirtley/svelte-doc-edit that referenced this issue Apr 27, 2023
Clarify documentation around when bind:group does and does not work. See issue sveltejs#2308
dummdidumm pushed a commit that referenced this issue Apr 27, 2023
Clarify documentation around when bind:group does and does not work. See issue #2308
@Rich-Harris Rich-Harris modified the milestones: 3.x, 5.0 Apr 1, 2024
@H40831
Copy link

H40831 commented Apr 12, 2024

Isn't it possible to resolve this by reimplementing bind:group?

<script>
  let checked = false;
  export let value = '';
  export let group = [];

  const handleCheck = () => { group = [...group, value] };
  const handleUncheck = () => { group = group.filter(checkedValue => checkedValue !== value) };

  $: checked? handleCheck(): handleUncheck();
</script>

<input type="checkbox" value={value} bind:checked>

and use it in the parent component

<script>
  let group = [];
</script>

<Checkbox value="xxx" bind:group={group} />

@webJose
Copy link

webJose commented Apr 12, 2024

Stopped by to support the idea of fixing this. Thanks!

My Scenario

A component library for a company-tailored Bootstrap. Would like to create a Checkbox component that handles all the Bootstrap things, and would like to provide the group property as part of the component. As it is right now, checkboxes behave like radio buttons.

@bestguy
Copy link

bestguy commented Apr 13, 2024

@webJose yes, this is what we ran into with Sveltestrap's Checkbox component for bootstrap when commented above.

@webJose
Copy link

webJose commented Apr 13, 2024

@bestguy the good thing, I suppose and thanks to @H40831, the behavior can be emulated pretty decently.

@katriellucas
Copy link

katriellucas commented Apr 20, 2024

I have found myself with the same problem, each solution seems to have some sort of downside, for example:

  • On @H40831 solution, although rarely, you might want to have some checkboxes with the same value for some reason, this is not possible because the Filter function will filter out repeated values. (The problem)
  • @iacore first aproach is quite nice, but as an index based solution, altering the array messes up everything: (The problem)

For now, using a "checked" prop seems less verbose while having none of the above downsides, but it doesn't respect the insert order either:

This is a Svelte 5 solution, but you can make it on Svelte 4 as well:

<!-- app.svelte -->

<script>
  import Checkbox from './Checkbox.svelte';
	
  let options = $state([
    { "value": "t1", "checked": false },
    { "value": "t2", "checked": false },
    { "value": "t3", "checked": false }
  ])
	
  let der = $derived(options.filter(o => o.checked === true).map(o => o.value))
</script>

<button onclick={() => options.unshift({ "value": "t"+options.length, "checked": false })}>add</button>
<button onclick={() => options = options.reverse()}>reverse</button>

<br><br>

{#each options as opt }
  <Checkbox value={opt.value} bind:checked={opt.checked}/>
{/each}

<br><br>

Checked {der}


<!-- Checkbox.svelte -->

<script>
  let { checked = $bindable(), value } = $props()
</script>

<label>
  {value}:<input type="checkbox" {value} bind:checked />
</label>

@iacore iacore linked a pull request Apr 20, 2024 that will close this issue
iacore pushed a commit to iacore/fix-svelte-bind-group that referenced this issue Apr 20, 2024
@iacore
Copy link

iacore commented Apr 20, 2024

@imbolc I fixed your example in #11256.

@webJose
Copy link

webJose commented Apr 21, 2024

@iacore I don't think this is what is needed. What we want is to be able to bind:group over t he nested component.

Instead of this:

{#each menu as flavour}
	<Flavour {flavour}/>
{/each}

<script>
	import Flavour from "./Flavour.svelte"
	let menu = [
		'Cookies and cream',
		'Mint choc chip',
		'Raspberry ripple'
	];
</script>

We want this (note the binding in the Flavour component):

{#each menu as flavour}
	<Flavour {flavour} bind:group={selection} />
{/each}

<script>
	import Flavour from "./Flavour.svelte"
	let menu = [
		'Cookies and cream',
		'Mint choc chip',
		'Raspberry ripple'
	];
        let selection = [];
</script>

@iacore
Copy link

iacore commented Apr 24, 2024

We want this (note the binding in the Flavour component):

{#each menu as flavour}
	<Flavour {flavour} bind:group={selection} />
{/each}

<script>
	import Flavour from "./Flavour.svelte"
	let menu = [
		'Cookies and cream',
		'Mint choc chip',
		'Raspberry ripple'
	];
        let selection = [];
</script>

This is fixed as well in #11256.

See PoC: https://github.com/iacore/fix-svelte-bind-group

https://github.com/iacore/fix-svelte-bind-group/blob/main/src/App.svelte

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.