I have a similar problem as the one shown in this Svelte REPL.
If I create a sortablejs object in a Svelte project from a dynamic list (that means a list created with {#each...}
logic blocks from a dynamic variable - stages
in the example), the job is done regarding the stages
variable (objects are moved correctly in the array) but the display is not correct after the move.
My guess is :
foo
when it's createdstages
array as expectedfoo
list is bind to the stages
arrayIt's like the move happened once in the stages
array and twice in the foo
list.
What I expect is that, after a move, the order of the foo
HTML list would be the same as the order of the stages
array.
I didn't find how to solve this issue. Any help/hind appreciated! Thank you!
EDIT
I thought the REPL was releavant enough to explain my problem but it was not. The key point of this issue is not the use of {#each...}
but the fact that stages
is dynamic.
Here is the code I working with :
<script lang="ts">
import Sortable from "sortablejs"
import ChipExo from "./ChipExo.svelte"
import { exercicesParams, moveExercice } from "../store"
import { onMount } from "svelte"
let listIdsForChips: string[] = []
$: {
listIdsForChips = []
for (const ex of $exercicesParams) {
listIdsForChips.push(ex.id ?? ex.uuid)
}
listIdsForChips = listIdsForChips
}
let chipsList: HTMLDivElement
onMount(() => {
chipsList = document.getElementById("chips-list")
const sortable = Sortable.create(chipsList, {
animation: 150,
onEnd: (evt) => {
exercicesParams.update((l) => {
return moveExercice(l, evt.oldIndex, evt.newIndex)
})
},
})
})
</script>
<div
class="w-full grid justify-items-stretch place-content-stretch grid-cols-3 sm:grid-cols-4 md:grid-cols-6 lg:grid-cols-7 xl:grid-cols-8 2xl:grid-cols-10 gap-2 p-0 items-center overflow-x-auto whitespace-nowrap"
id="chips-list"
>
{#each listIdsForChips as id, indice (indice)}
<ChipExo text={id} {indice} />
{/each}
</div>
and here is the display in HTML
The listIdsForChips
variable is reset every time the exercicesParams
store changes. For example, Chips can be removed by clicking on the close icon of the chip. So the listIdsForChips
variable has to be dynamic.
I hope this edit will clarify the issue.
In the first example stages
doesn't depend on any other value, so just let stages = ...
without reactive declaration
When adding a key to the #each
block it seems to be working correctly - tutorial
{#each stages as stage (stage.data)}
<li>{stage.data}</li>
{/each}
In your second example, instead of setting the index as key
{#each listIdsForChips as id, indice (indice)}
<ChipExo text={id} {indice} />
{/each}
since you map to the id or uuid take the value instead
{#each listIdsForChips as id, indice (id)}
<ChipExo text={id} {indice} />
{/each}
or if there's no unique field - from the tutorial explanation
You can use any object as the key, as Svelte uses a Map internally — in other words you could do (thing) instead of (thing.id). Using a string or number is generally safer, however, since it means identity persists without referential equality, for example when updating with fresh data from an API server.
The second problem with the reactive statements seems to be with the first line - changing to a second variable name seems to be one fix
let listIdsForChips: string[] = []
$: {
let lIFC = []
for (const ex of $exercicesParams) {
lIFC.push(ex.id ?? ex.uuid)
}
listIdsForChips = lIFC
}
To reduce unpredicted behaviour and make the dependencies clearer, wrapping in a function is an alternative way
let listIdsForChips: string[] = []
$: updateListIdsForChips($exercicesParams)
function updateListIdsForChips(eP) {
let lIFC = []
for (const ex of eP) {
lIFC.push(ex.id ?? ex.uuid)
}
listIdsForChips = lIFC
}
or in this case reducing to one line
let listIdsForChips: string[] = []
$: listIdsForChips = $exercicesParams.map(p => p.id ?? p.uuid)
I updated the REPL