Svelte child component checkbox does not reflect props

176 views Asked by At

I've come unstuck while building my first Svelte project. I've built a tree-view component in which each node has a checkbox; checkbox-change values should be passed down to the child nodes.

My tree-view consists of two components (TreeNode is a class which contains, for example the text which should appear on each node):

<!-- TreeView.svelte -->
<script lang="ts">
    import TreeNode from "./TreeNode";
    import TreeNodeView from "./TreeNodeView.svelte";

    export let isChecked: boolean = false;
    export let treeNodes: Array<TreeNode>;
</script>

<ul role="group">
    {#each treeNodes as treeNode ( treeNode.id )}
        <TreeNodeView {isChecked} {treeNode} />
    {/each}
</ul>
<!-- TreeNodeView.svelte -->
<script lang="ts">
  import ...

  let hasFocus: boolean = false;

  function onChecked(e: CustomEvent<boolean>) {
    isChecked = e.detail;
  }

  export let isChecked: boolean;
  export let treeNode: TreeNode;

  $:{
    console.log(`${treeNode.text}.isChecked = ${isChecked}`);
  }
</script>

<li>
    </div>
  {:else}
    <div>&nbsp;</div>
  {/if}
  {#if treeNode.children.length === 0}
    <Link href="~"><Checkbox labelText={treeNode.text} /></Link>
  {:else}
    <Checkbox checked={isChecked} labelText={treeNode.text} id={treeNode.id} name={treeNode.id} on:check={onChecked} />
  {/if}
  {#if treeNode.isSelected && treeNode.children.length}
    <TreeView treeNodes={treeNode.children} {isChecked} />
  {/if}
</li>

In the reactive block ($: { ... }) it appears that the isChecked value is being passed down, but none of the checkboxes reflect the updated value. I.e., when checking de-CH in the following node...

enter image description here

I see this in the console...

enter image description here

... but the two child-node checkboxes remain stubbornly unchecked.

What have I missed?

[The styled components I'm using are carbon-components-svelte.]

UPDATE:
I realised that I had omitted the checked binding in one of the checkboxes: In my TreeNodeView.svelte component I needed to add...

{#if treeNode.children.length === 0}
  <Link href="~">
    <Checkbox labelText={treeNode.text} checked={isChecked} /> // << checked={isChecked}
  </Link>
{:else}
  // ...
{/if}

What a silly mistake - sorry to anyone who gave this any thought.

1

There are 1 answers

2
Keyboard Corporation On

If the change is not being reflected in the UI, it could be due to the way the checkbox component is handling its checked prop. If the checkbox component is not properly reacting to changes in it's checked prop, it might not update its internal state or the DOM correctly, even though the prop itself is being updated.

This way is to ensure that the checkbox component updates correctly when it's checked prop changes. It is the use of two-way binding. You can use the bind:checked directive to create a two-way binding between the checked prop and the checkbox's internal state. This means that changes to the checkbox's state will update the checked prop, and changes to the checked prop will update the checkbox's state.

Example two-way binding for this;

<Checkbox bind:checked={isChecked} labelText={treeNode.text} id={treeNode.id} name={treeNode.id} on:check={onChecked} />

This should ensure that the checkbox's state is updated whenever the isChecked prop changes, and vice versa.

If still problem with the above, It could be that the checkbox component is not properly handling the on:check event. You could try using Svelte's built-in on:change event instead, which is triggered whenever the checkbox's state changes. You could then update the isChecked prop in the event handler.

Example;

<Checkbox checked={isChecked} labelText={treeNode.text} id={treeNode.id} name={treeNode.id} on:change={e => isChecked = e.target.checked} />

This will ensure that the isChecked prop is updated whenever the checkbox's state changes, which should cause the component to re-render with the updated state.

Edit (As additional issue found.)

In your TreeNodeView.svelte component, you're using a CustomEvent to update the isChecked value. However, this event is only being listened to in the TreeNodeView component itself, and not being passed up to the parent TreeView component or any child components. This means that when you check a box, the isChecked value is updated in the TreeNodeView component, but this change isn't reflected in the parent or child components.

If so, you'll need to dispatch an event from the TreeNodeView component whenever the checkbox is clicked, and listen for this event in the parent TreeView component. When the event is fired, the parent component should update the isChecked value for the corresponding TreeNode and pass this new value down to the child components.

In TreeNodeView.svelte, dispatch an event, so whenever a checkbox is clicked, the isChecked value should be updated in the parent TreeView component and passed down to the child components, updating the state of the checkboxes accordingly.

<script lang="ts">
 // ...

 function handleCheck(e: CustomEvent<boolean>, treeNode: TreeNode) {
   treeNode.isChecked = e.detail;
 }

 // ...
</script>

<ul role="group">
   {#each treeNodes as treeNode ( treeNode.id )}
       <TreeNodeView {isChecked} {treeNode} on:check={(e) => handleCheck(e, treeNode)} />
   {/each}
</ul>