'Svelte - is there a way to use JS inside #each?
I'm iterating over an array of data and I want to do some processing on it before rendering it.
I know that I could create a new component and pass array entry to i, and do the processing within that sub-component, or I could add a helper function getClass(entry)
or I could inline a tenary operator,
but I'm wondering if there's a way to do something like this, to inline some code into the each block. Non-functional example:
<div class="Menu">
{#each menuEntries as entry, i }
{{
let classes = entry.classes;
if (entry.submenu) {
classes += ' has-submenu';
}
}}
<div class="menu-entry {classes}">...</div>
{/each}
</div>
Edit:
It seems like a workaround like this works. The only thing is that classes
have to be defined before the loop.
<script>
let classes = '';
</script>
<div class="Menu">
{#each menuEntries as entry, i }
{(() => {
classes = entry.classes;
if (entry.submenu) {
classes += ' has-submenu';
}
return ''; // return empty string so Svelte does not print it
})()}
<div class="menu-entry {classes}">...</div>
{/each}
</div>
Solution 1:[1]
You can use an Array.map function to do some additional processing.
This way you can add an optional argument using the map "this" and use additional loop variables using [....] returned from the map function.
Example:
{#each menuEntries.map(extraProcessing, thisArg) as [entry, arg2, arg3] , i }
... loop using entry, arg2, arg3, i
{/each}
Example extraProcessing function:
function extraProcessing(entry, idx) {
... do something using: entry, idx and this (thisArg)
return [entry, arg2, arg3]
And a here a REPL with your example.
Solution 2:[2]
For those who need to do some calculations inside the #each
block you can use @const
statement.
<script>
const items = ['a', 'b', 'c'];
</script>
{#each items as item}
{@const exclamationMark = item + '!'}
<p>{exclamationMark}</p>
{/each}
Solution 3:[3]
if all you want is activate a class if entry has a submenu property you could use a conditional class this
<style>
.has-submenu {/* your conditional css */}
</style>
<div class="Menu">
{#each menuEntries as entry, i }
<div
class={"menu-entry " + entry.classes}
class:has-submenu={entry.submenu}
>
...
</div>
{/each}
</div>
just beware, class:has-submenu={entry.submenu}
evaluates to true and activates the class only if entry.submenu is truthy itself (not null, undefined, 0, etc), so if that's a problem you should directly check whether the property is there
Solution 4:[4]
Another solution is to a Svelte component to process data and return results using slots.
you can add a component named Process.svelte which receives args and function, the result of calling the function will be provided in a slot prop.
<script>
export let args = {}
export let process
</script>
<slot res={process(args)}/>
import Process.svelte
<script>
import Process from './Process.svelte'
let menuEntries = []
</script>
<div class="Menu">
{#each menuEntries as entry, i }
<Process process={() => {
let a= entry.classes;
if (entry.submenu) {
a+= ' has-submenu';
}
return a; // the return value can be accessed by the children via let:res
}} let:res>
<div class="menu-entry {res}">...</div>
</Process>
{/each}
</div>
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | Roman Mahotskyi |
Solution 3 | skpn |
Solution 4 | Ryuman |