1

In my application I have a select that I use to choose an item. When I delete an item from the array I want to update the list but I cannot find a way to do it

REPL

App.svelte

<script>
    import Car from './Cars.svelte'
    import MaintainCar from './MaintainCar.svelte'
    import { cars } from './store.js'
</script>

    <Car {cars}/>
    <MaintainCar {cars} />

Cars.svelte

<script>
    export let cars
    export let selected_car = ''
    let colour = ''
    let car_list = getCars($cars)
    $: colour = $cars[selected_car]

    function getCars(cars) {
        let car_list = []
        for (const [key, value] of Object.entries(cars)) {
            car_list.push(key);
        }
        if (!selected_car) {
            selected_car = car_list[0]
            colour = cars[selected_car]
        }
        return car_list
    }

    function selectCar() {
        colour = $cars[selected_car]
    }
</script>

<select bind:value={selected_car} on:click={selectCar}>
    {#each car_list as car}
        <option value={car}>
            {car}
        </option>
    {/each}
</select>
<input type="text" bind:value={colour} placeholder="colour"/>

MaintainCars.svelte

<div id='maintain'>
    <input type="text" bind:value={car} placeholder="car"/>
        <input type="text" bind:value={colour} placeholder="colour"/>
    <div>
        <button class="select-button" on:click={deleteCar}>Delete</button>
    </div>
</div>

<script>
    export let cars
    let car = ''
    $: colour = checkCar(car)

    function checkCar(car) {
        colour = '';
        if (car && car in $cars) {
            colour = $cars[car];
        }
        return colour
    }

    function deleteCar() {
        if (car in $cars) {
            delete $cars[car]
            car = ''
            colour = ''
                        console.log($cars)
        }
    }
</script>   

store.js

import { writable } from 'svelte/store'

export const cars = writable({'Audi': 'red', 'BMW':'blue', 'Hillman': 'green'})
Psionman
  • 3,084
  • 1
  • 32
  • 65

2 Answers2

3

You can do it by re-assigning $cars when you delete, and mark the car_list assignment as a reactive statement.

in MaintainCar.svelte:

function deleteCar() {
    if (car in $cars) {
        delete $cars[car]
        car = ''
        colour = ''
        $cars = $cars
    }
}

in Cars.svelte:

let car_list
$: car_list= getCars($cars)

For example: https://svelte.dev/repl/3c08db46c34c449f90b75df015c0fa01?version=3.46.4

CD..
  • 72,281
  • 25
  • 154
  • 163
1

One way to update store listeners when you delete an item from an object is to create a custom store with delete as a method on the store that updates the object by deleting the item at the provided key. You have to change the way you delete items from objects to your method instead of the JavaScript delete keyword, but this updates listeners without reassigning your store every time.

REPL: Delete car - custom store

In store.js, write a function to create a custom store:

export function createObjectStore(obj) {
    const objStore = writable(obj);
    
    return {
        // passes the writable store's methods through - subscribe, update, set
        ...objStore,
        // adds a new delete method to the store
        delete: (key) => {
            // Delete the item at the provided key from this object and update the store
            objStore.update(objVal => {
                delete objVal[key];
                return objVal;
            })
        }
    }
}

Then you can change your writable store for cars to the new custom store:

export const cars = createObjectStore({'Audi': 'red', 'BMW':'blue', 'Hillman': 'green'})

Then change the way you delete the selected car to the custom store method in MaintainCar.svelte line 24:

// Previously, we used the delete keyword
// delete $cars[car]
// Now, we will use the custom store's delete method
cars.delete(car)

As CD.. mentioned, you need to make car_list reactive in Cars.svelte line 5:

$: car_list = getCars($cars)

Now, you can delete items from your cars object with the cars.delete method, and the store will update accordingly. I hope this helps!

Inspiration: this answer about updating arrays in stores that mentions Rich Harris's REPL about creating a custom store that wraps arrays.


PS: In this REPL: Delete car - custom store expanded, I made a number of other tweaks as well. I created carsBeforeStore to demonstrate that the store's object stays up-to-date outside the store. I added a couple of buttons to demonstrate some other operations on the store. I made some other tweaks as well including making the select revert to the first car in cars if selected_car was deleted in Cars.svelte in the getCars function (only changed line 13, but the rest of the block is reproduced for readability):

// If selected_car is no longer in cars, select the first car
if (!selected_car || !(selected_car in cars)) {
    selected_car = car_list[0]
    colour = cars[selected_car]
}
TJ Couch
  • 11
  • 3