I'm working on some code where I fetch
some items in onMount
then render them. I don't want to play a ton of transitions for the big batch of fetch
ed items. However, I do want to play transitions when individual items are added or removed from the list. This is a similar situation to this question.
If I write this, then I get the transition behavior I want.
{#if itemsFetched}
{#each items as it (it.id)}
<div transition:slide|local>
<span>{it.id} {it.name}</span>
<button on:click={removeItem(it)}>remove</button>
</div>
{/each}
{/if}
However, inside of the each
block, I need to render the items 1 of 2 ways. For this example, I render even IDs one way and odd IDs another. If I add an if
block, then the individual item transitions don't play at all.
{#if itemsFetched}
{#each items as it (it.id)}
{#if it.id%2 === 0}
<div transition:slide|local>
<span>render like this {it.id} {it.name}</span>
<button on:click={removeItem(it)}>remove</button>
</div>
{:else}
<div transition:slide|local>
<span>render this other way {it.id} {it.name}</span>
<button on:click={removeItem(it)}>remove</button>
</div>
{/if}
{/each}
{/if}
Here's a full example. Sandbox: https://codesandbox.io/s/crazy-heyrovsky-bwviny?file=/App.svelte
<script>
import { slide } from "svelte/transition";
import { onMount } from "svelte";
// Initially we have no items.
let items = [];
let id = 0;
let itemsFetched = false;
onMount(() => {
// Fetch items from API.
items = [
{id: id, name: `item ${id++}`},
{id: id, name: `item ${id++}`},
{id: id, name: `item ${id++}`},
];
itemsFetched = true;
});
function addItem() {
items = [
...items,
{id: id, name: `item ${id++}`},
];
}
function removeItem(rmIt) {
return () => {
items = items.filter(it => it.id !== rmIt.id);
};
}
</script>
<div>
<button on:click={addItem}>add</button>
{#if itemsFetched}
{#each items as it (it.id)}
{#if it.id%2 === 0}
<div transition:slide|local>
<span>render like this {it.id} {it.name}</span>
<button on:click={removeItem(it)}>remove</button>
</div>
{:else}
<div transition:slide|local>
<span>render this other way {it.id} {it.name}</span>
<button on:click={removeItem(it)}>remove</button>
</div>
{/if}
{/each}
{/if}
</div>
Why does adding an if
block break the local
transition? If I remove local
, then the items play transitions when added or removed, but then I lose the initial onMount
behavior I want.
local
restricts the transition to the creation/destruction of the block the element is in, which is the {#if it.id%2 === 0}
. But since the ID presumably never changes, the block just always exists, i.e. there is no change and hence no animation is triggered.
Put the {#if}
inside the element with the transition.
Instead of this:
{#each items as it (it.id)}
{#if it.id%2 === 0}
<div transition:slide|local>
<span>render like this {it.id} {it.name}</span>
<button on:click={removeItem(it)}>remove</button>
</div>
{:else}
<div transition:slide|local>
<span>render this other way {it.id} {it.name}</span>
<button on:click={removeItem(it)}>remove</button>
</div>
{/if}
{/each}
Do this:
{#each items as it (it.id)}
<div transition:slide|local>
{#if it.id%2 === 0}
<span>render like this {it.id} {it.name}</span>
<button on:click={removeItem(it)}>remove</button>
{:else}
<span>render this other way {it.id} {it.name}</span>
<button on:click={removeItem(it)}>remove</button>
{/if}
</div>
{/each}