Home > Software engineering >  How do I get values from components added by for each in svelte?
How do I get values from components added by for each in svelte?

Time:01-03

I'm new to front end and svelte and experimenting with some things.

I have the following counter component that has increment/decrement button for the items:

Counter.svelte

<script>
    export let amount = 0;
    function increment() {
        amount  = 1;
    }

    function decrement() {
        if (amount > 0) {
            amount -= 1;
        }
    }
</script>


<button on:click={decrement}>-</button>{amount}<button on:click={increment}> </button>

App.svelte

<script>
... import stuff

    let items=[{ //data from rest
      id: 1,
      name: potato,
      price: 5
    }, {
      id: 2,
      name: garlic,
      price: 3
    }, {
      id: 3,
      name: rice,
      price: 10
    }];

    function purchase() {
        //TODO use JSON generated in POST
    }
</script>

{#each items as item}
    {item.name} <Counter></Counter>
{/each}

<button on:click={purchase}>Purchase</button>

How do I generate the following JSON, when the amount selected for the item is not 0 after I click the button? (assuming garlic amount is 0)

    [{
      id: 1,
      name: potato,
      price: 5,
      amount: 30
    },{
      id: 3,
      name: rice,
      price: 10,
      amount: 400
    }];

CodePudding user response:

It's possible to bind a value inside the #each loop to a property of the single item

{#each items as item}
    {item.name} <Counter bind:amount={item.amount}/>
{/each}

The property doesn't exist yet but since you have a default value set inside Counter

export let amount = 0;

it will be initialized with zero. All changes to the amounts will then change items directly. So inside purchase just filter items for the ones with an amount

    function purchase() {
        const itemsWithAmount = items.filter(i => i.amount > 0)
        console.log(itemsWithAmount)
    }

REPL

<script>
    import Counter from './Counter.svelte'

    let items=[{
        id: 1,
        name: 'potato',
        price: 5
    }, {
        id: 2,
        name: 'garlic',
        price: 3
    }, {
        id: 3,
        name: 'rice',
        price: 10
    }];

    $: console.log(items)

    function purchase() {
        const itemsWithAmount = items.filter(i => i.amount !== 0)
        console.log(itemsWithAmount)
    }
</script>

<ul>
    {#each items as item}
    <li>
        {item.name} <Counter bind:amount={item.amount}/>
    </li>
    {/each}
</ul>

<button on:click={purchase}>Purchase</button>

CodePudding user response:

You probably would want to initialize the amount property of all the items to 0 after getting the data, then it can be bound to the Counter:

<Counter bind:amount={item.amount} />

bind propagates changes from the component to the item objects.

In purchase you then just have to filter the items, something like:

const toBuy = items.filter(x => x.amount > 0);
  • Related