webc icon indicating copy to clipboard operation
webc copied to clipboard

Can't access data in nested `webc:for` loops

Open monochromer opened this issue 1 year ago • 4 comments

There are data that need to be output in the form of a table - an array of people and an array of fields

// index.11tydata.js

module.exports = {
  users: [
    {
      id: 1,
      name: 'Alex',
      email: '[email protected]'
    },
    {
      id: 2,
      name: 'Bob',
      email: '[email protected]'
    },
    {
      id: 3,
      name: 'Steave',
      email: '[email protected]'
    }
  ],

  columns: ['id', 'name', 'email']
}

There is a component to which I pass data:

<c-data-table :@columns="columns" :@items="users"></c-data-table>

Here is the implementation of the component:

<table>
  <thead>
    <tr>
      <th webc:for="column of columns" @text="column"></th>
    </tr>
  </thead>
  <tbody webc:if="items?.length > 0">
    <tr webc:for="item of items">
      <td webc:for="column of columns" @text="item[column]"></td>
    </tr>
  </tbody>
</table>

I get an error about an unavailable variable item:

[11ty] 1. Having trouble rendering webc template ./src/pages/index/index.webc (via TemplateContentRenderError)
[11ty] 2. Check the dynamic attribute: `@text="item[column]"`.
[11ty] Original error message: Cannot read properties of undefined (reading 'id') (via Error)
[11ty] 
[11ty] Original error stack trace: Error: Check the dynamic attribute: `@text="item[column]"`.

monochromer avatar Apr 26 '23 15:04 monochromer

Hey @monochromer,

Very late but I just ran into the same issue as you -- it seems that with nested webc:for loops, a child element with a for loop on it can't access any loop values from a parent loop -- only current loop and global variables.

So in your instance for <td webc:for="column of columns" @text="item[column]"></td>, you're able to have the column value but not the item value since it's in the parent loop.

Having spent the last day looking at this, luckily it seems JavasScript render functions can solve this(alternatively you could use template syntax as well).

Since the JavaScript render has access to all variables in the stack, what worked for me and I would imagine should work for you is to replace:

<tr webc:for="item of items">
    <td webc:for="column of columns" @text="item[column]"></td>
</tr>

with

<tr webc:for="item of items">
  <script webc:type="js">
    let html = '';
    for(column of columns){
      html += `<td>${item[column]}</td>`;
    }
    html;
  </script>
</tr>

Just for awareness -- it seems webc has some issues with table elements as well. Probably worth reading my other issue #183 and PR

sc0ttes avatar Sep 16 '23 16:09 sc0ttes

I just ran into this, and found it very unitnuitive, and kind of a deal-breaker for using webc components over any other template type or plain javascript for more complex use cases ( and lit for proper web components ).

Don't take that as whining, so much as just perspective - i think this is an issue that should be addressed asap. I have started to play with the source a little myself but I think the best thing I can do to help is put in a documentation PR to clarify this and maybe reference this issue in the meantime.

The flexibility of 11ty leaves some decent work-arounds in the meantime; particularly webc:type="js" if we want to stick with a webc-centered setup

refactorized avatar Sep 09 '24 13:09 refactorized

https://github.com/11ty/11ty-website/pull/1725

refactorized avatar Sep 09 '24 15:09 refactorized

Maintainer's note: Once this is resolved, https://github.com/11ty/11ty-website/pull/1725 needs to be reverted/updated.

Snapstromegon avatar Sep 09 '24 17:09 Snapstromegon

I ran into this issue today. After trying a bunch of different things, I just discovered that the loops work if you break them out into their own components. Clunky, but it's working for me.

<section>
    <div class="container mx-auto px-5 py-10 xl:px-64 xl:py-20">
        <h2 class="mb-8">Swatches</h2>
        <swatch-list :@colors="data.colors"></swatch-list>
    </div>
</section>
<!-- swatch-list.webc -->
<div webc:for="color in colors" webc:nokeep>
    <h3 @text="color"></h3>
    <div class="flex space-x-4 mb-8 last:mb-0">
        <swatch :@color="color" :@shades="colors[color]"> </swatch>
    </div>
</div>
<!-- swatch.webc -->
<div :class="'bg-' + color + '-' + shade" webc:for="shade in shades">
    <p @text="shade"></p>
</div>

mjawn avatar Sep 27 '24 00:09 mjawn

I just discovered that the loops work if you break them out into their own components

In your exmaple, color and colors has one scope.

monochromer avatar Sep 27 '24 04:09 monochromer