Svelte - is there a way to use JS inside #each?
Asked Answered
R

4

10

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>


Rep answered 28/4, 2021 at 9:9 Comment(2)
<div class="menu-entry {entry.classes} {entry.submenu?'has-submenu':''}">...</div>Hartsfield
The "edit" solution above (with the code inside a IIFE) actually freezes the whole browser. The @voscausa solution (with the code inside the "map"), instead, works perfectly if you want to add custom code inside the loop.Unhelm
D
14

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}
Dett answered 7/5, 2022 at 12:39 Comment(0)
S
6

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.

Sphygmomanometer answered 28/4, 2021 at 10:41 Comment(1)
Genius! Actually, it looks as the only way to really add custom code inside a Svelte loop. Thanks. :)Unhelm
S
1

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

Southsouthwest answered 29/4, 2021 at 21:58 Comment(0)
S
1

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>
Stateroom answered 1/5, 2021 at 5:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.