Reputation: 2999
I have what I believe is a quite small and simple svelte webapp, which is currently just a questionnaire. And I am trying to create a sort of multiple select system, where you can click a bunch of buttons and have them marked as selected (pretty much an html checkbox but it is slightly beside the point).
So in order to show to the user that an option is selected, I want to add a class to button if it is selected. However it is not being updated, and I can't really figure out why.
The isSelected
function is only called once per option and never when the setup object is modified, and I am wondering why that is
<script>
let page = 0;
let setup = {
JLPT: {
question: "select JLPT levels",
options: [1, 2, 3, 4, 5, 6, 7, 8, 9],
selected: [],
onSelect: option => {
console.log(setup.JLPT.selected);
setup.JLPT.selected = setup.JLPT.selected.concat(option);
}
}
};
$: question = getQuestion(page);
function getQuestion(page) {
return setup[Object.keys(setup)[page || 0]];
}
function isSelected(option) {
return (question.selected || []).indexOf(option) > -1;
}
function onClick(option) {
getQuestion(page).onSelect(option);
}
</script>
<div class="wrapper">
<div class="question">{question.question}</div>
<div class="input">
{#each question.options as option}
<button
on:click={() => onClick(option)}
class={isSelected(option) ? 'selected' : ''}>
{option}
</button>
{/each}
</div>
</div>
Upvotes: 3
Views: 2496
Reputation: 4266
Replace $: question = getQuestion(page);
with:
$: question = setup[Object.keys(setup)[page || 0]];
https://svelte.dev/repl/f7c2a4119829442a8e46ebb557bf1cf8?version=3.29.4
The way Reactive declarations work, is that svelte parses it and extracts the parameters it depends on. In the case of $: question = getQuestion(page);
the dependencies are getQuestion
and page
, and whenever any of them changes, question
will be recomputed.
But getQuestion
and page
never change, so question
is never recomputed.
So what you want, is question
to be recomputed whenever setup
changes, and to do that you need to make sure question
depends on setup
.
In other words, you need the Reactive declarations to have this form
$: question = ...setup...
That principle also happens in this line
class={isSelected(option) ? 'selected' : ''}
Svelte parses it, and extracts as dependencies isSelected
and option
, and recomputes the "class attributes" whenever any of them changes.
But now you might think ... Hey! but they never change, so how is this even working?.
The reason is that option
is part of the #each block
that depends on question
which in turns depends on setup
.
So whenever setup
changes (marked as "dirty") question
is recomputed and marked as "dirty", and thus each "class attribute" is recomputed because indirectly they all depend on question
.
BTW: a better solution to assign classes dynamically is using the class directive
class:selected={isSelected(option)}
You might want to read this post https://lihautan.com/compile-svelte-in-your-head-part-2/#reactive-declaration to get a better feeling of what Svelte is doing under the hood in Reactive declarations
Upvotes: 5