11ty / webc

Single File Web Components
MIT License
1.3k stars 36 forks source link

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

Open monochromer opened 1 year ago

monochromer commented 1 year ago

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: 'alex@example.com'
    },
    {
      id: 2,
      name: 'Bob',
      email: 'bob@example.com'
    },
    {
      id: 3,
      name: 'Steave',
      email: 'steave@example.com'
    }
  ],

  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]"`.
sc0ttes commented 9 months ago

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