0

I have a Svelte store with an array of Comment objects, each of which can have a child array of Comment objects (replies), which can also have their own replies, creating a tree-like structure of arbitrary depth.

The answer to this question was very helpful in explaining how to use 2-way bindings to be able to update properties on an individual object within this type of structure (e.g. to edit the comment message or add new replies).

What I can't figure out is how to implement a delete button on each comment that will delete the comment from the store (along with all it's children).

Link to REPL with the code shown below

App.svelte

<script>
    import { comments } from './stores.js';
    import Comment from './Comment.svelte';
</script>

<div>
    {#each $comments as comment (comment.id)}
        <Comment
            id={comment.id}
            bind:message={comment.message}
            bind:replies={comment.replies}
        />
    {/each}
</div>

<style>
    div {
        padding: 10px;
        display: flex;
        flex-direction: column;
        gap: 12px;
        max-width: 500px;
    }
</style>

Comment.svelte

<script>
    export let id;
    export let message;
    export let replies = [];
    
    const deleteComment = () => {
        alert(`Delete comment ${id}`)
    }
</script>

<div class="card">
        <p>{message}</p>
        <button on:click={deleteComment}>Delete</button>
</div>

{#if replies.length}
    <div class="reply-container">
        {#each replies as reply (reply.id)}
            <svelte:self
                id={reply.id}
                bind:message={reply.message}
                bind:replies={reply.replies}
            />
        {/each}
    </div>
{/if}

<style>
    p {
        font-size: 0.875rem;
    }
    button {
        font-size: 0.875rem;
        margin: 0;
    }
    .card {
        border: 1px solid grey;
        border-radius: 6px;
        display: flex;
        justify-content: space-between;
        align-items: center;
        padding: 0 14px;
        
    }
    .reply-container {
        padding-left: 36px;
        border-left: 1px solid grey;
        display: flex;
        flex-direction: column;
        gap: 12px;
    }
</style>

stores.js

import { writable } from 'svelte/store';

export const comments = writable([
    {
        "id": 1,
        "message": "Comment message",
        "replies": [
            {
                "id": 2,
                "message": "Comment message",
                "replies": [
                    {
                        "id": 3,
                        "message": "Comment message",
                        "replies": []
                    },
                    {
                        "id": 4,
                        "message": "Comment message",
                        "replies": [
                            {
                                "id": 5,
                                "message": "Comment message",
                                "replies": []
                            }
                        ]
                    }
                ]
            },
            {
                "id": 6,
                "message": "Comment message",
                "replies": [
                    {
                        "id": 7,
                        "message": "Comment message",
                        "replies": []
                    }
                ]
            }
        ]
    },
    {
        "id": 8,
        "message": "Comment message",
        "replies": [
            {
                "id": 9,
                "message": "Comment message",
                "replies": []
            }
        ]
    },
    {
        "id": 10,
        "message": "Comment message",
        "replies": []
    }
]);
  • What exactly is the problem, what have you tried? (Also, that link to a question does not lead to a question.) – H.B. Jan 29 '23 at 21:30
  • Apologies, I've fixed the link to the other question I referenced. I was getting stuck trying to figure out how to add a 'delete' button to each comment that would remove it from the store. I had been trying to import the store within the Comments component and then manipulate it from there, but it wasn't quite working. Corrl's answer seems to be was what I was missing. It looks like I needed to pass the whole comments store as a bound prop to the Comments component. – Aaron Rubinstein Jan 30 '23 at 00:33

1 Answers1

1

When also passing the comments as prop, each comment could remove itself with all the children >> REPL

App.svelte
<script>
    import { comments } from './stores.js';
    import Comment from './Comment.svelte';
</script>

<div>
    {#each $comments as comment (comment.id)}
    <Comment id={comment.id}
                     bind:message={comment.message}
                     bind:replies={comment.replies}
                     bind:comments={$comments} />
    {/each}
</div>

<style>
    div {
        padding: 10px;
        display: flex;
        flex-direction: column;
        gap: 12px;
        max-width: 500px;
    }
</style>
Comment.svelte
<script>
    export let id
    export let message
    export let replies
    export let comments

    const triggerDelete = () => {
        comments = comments.filter(c => c.id !== id)
    }
</script>

<div class="card">
    <p>{id} - {message}</p>
    <button on:click={triggerDelete}>Delete</button>
</div>

{#if replies.length}
<div class="reply-container">
    {#each replies as reply (reply.id)}
    <svelte:self 
                             id={reply.id}
                             bind:message={reply.message}
                             bind:replies={reply.replies}
                             bind:comments={replies}
                             />
    {/each}
</div>
{/if}

<style>
    p {
        font-size: 0.875rem;
    }
    button {
        font-size: 0.875rem;
        margin: 0;
    }
    .card {
        border: 1px solid grey;
        border-radius: 6px;
        display: flex;
        justify-content: space-between;
        align-items: center;
        padding: 0 14px;

    }
    .reply-container {
        padding-left: 36px;
        border-left: 1px solid grey;
        display: flex;
        flex-direction: column;
        gap: 12px;
    }
</style>
Corrl
  • 6,206
  • 1
  • 10
  • 36