I recently started to look into Svelte and wanted to create a component called SelectorTable
which allows me to select the rows of table by clicking on them. A selected row is supposed to get highlighted in a different color.
I tried using the build in class directive
.
<script lang="ts">
type ObjectKey = keyof any;
export let data: any[] = [];
export let key = 'id' as ObjectKey;
let selected: Set<ObjectKey> = new Set<ObjectKey>();
function toggle(id: ObjectKey) {
if (selected.has(id)) {
selected.delete(id);
} else {
selected.add(id)
}
console.log(selected)
console.log(selected.has(id))
}
</script>
<table>
<thead>
<tr>
{#each Object.keys(data[0]) as heading}
<th>{heading}</th>
{/each}
</tr>
</thead>
<tbody>
{#each Object.values(data) as row}
<tr class:tr-selected={selected.has(row[key])} on:click={() => {toggle(row[key])}}>
{#each Object.values(row) as cell}
<td>{cell}</td>
{/each}
</tr>
{/each}
</tbody>
</table>
<style>
table, th, td {
border: 1px solid;
border-collapse: collapse;
margin-bottom: 10px;
}
tr:hover {
background-color: red;
cursor: pointer;
}
.tr-selected {
background-color: greenyellow;
}
</style>
The on:click
function toggle
works and correctly adds or removes the selected keys. The problem I have is that the background color of the selected rows are not changing even if selected.has(row[key]) == true
. Here is the console output when clicking on a row with the key 1
twice.
> Set(1) {1} ------- SelectorTable.svelte:14
> true ------------- SelectorTable.svelte:15
> Set(0) {size: 0} - SelectorTable.svelte:14
> false ------------ SelectorTable.svelte:15
Firstly the key is added to the selected set and then it is removed again.
So if the output of selected.has(row[key])
is true why is the class directive not working and the color of the row not changing?
2
Answers
I have found a solution. Not sure if there is a better one but this one works.
After trying out a bit I realized that Svelte was not checking
selected.has(row[key])
in the class directive and was therefore not rerendering the row. I then tried svelteskey
logic block by passing theselected
set into it but it still did not work.Lastly I added a new boolean variable
update
which I reference in thekey
block and every time the functiontoggle
is called update gets flipped.Correct me if I am wrong.
I assume the reason for this behaviour is that JS/TS passes objects and functions like the
selected
ortoggle
by reference and primitive variables like ints or booleans by value.Sveltes documentation of
key
statesThis would explain why the row did not rerender when using
selected
. Because it is passed by reference and references are primitive types as well and since only the value behind the reference changed and not the reference itself Svelte does not recognize the change and therefor does not rerender.This same behaviour is not stated specifically in the docs of class directives but it's save to assume that it will be similar.
Reactivity is based on assignments, so if you use something like a
Set
which is modified via functions, you can just add a dummy assignment after the change to trigger updates wherever the set is being used: