Alpine: [Documentation] x-for

Created on 23 Jan 2020  路  8Comments  路  Source: alpinejs/alpine

I'm struggling a bit with x-for.

Looking at the source, it seems that the full syntax for the x-for attribute is:
(item,index,collection) in expression
where the expression is intended to return an array, or at least something that implements forEach(function(v,i,g){...})

But when I try to use this like so:

<ul>
<template x-for='(value,index,coll) in toggles'>
    <li>Index:<span x-text='index'></span> Value: <span x-text='value'></span></li>
</template>
</ul>

The result is an error is thrown:

VM6568:3 Uncaught (in promise) ReferenceError: index is not defined
    at eval (eval at n (utils.js:61), <anonymous>:3:27)
    at n (utils.js:61)
    at x.evaluateReturnExpression (component.js:246)
    at component.js:205
    at Array.forEach (<anonymous>)
    at x.resolveBoundAttributes (component.js:191)
    at x.initializeElement (component.js:156)
    at component.js:137
    at component.js:128
    at e (utils.js:36)

Additionally, I'm not exactly clear on the usage of the :key='xxx' attribute.

The x-for implementation code seems to use it if it is present to uniquely identify items in the list to detect when they are merely moving instead of appearing or disappearing. So in the case where I'm iterating throug a list of scalars (boolean, number, string) where there might be duplicates, it would seem that :key='index' might be a good idea.

Presumably if the array were an array of objects and the object had a natural "unique id", you would do something like x-for='item in items' :key='item.id'. Is that the intended usage (I'm also a newbie at Vue, so if the answer is "it's just like Vue" then that's not really helpful.)

So, it would be nice to have an expansion of the x-for example that uses all the bells and whistles:

  • 3-item LHS for in
  • :key
  • use all three items (value,index,collection) in the body of the x-for

Thanks! This seems to be just the library I'm looking for!

Most helpful comment

The magic variable was an initial suggestion but it's actually better to use the same syntax as VueJs (x-for="(value, index) in items").

Less magic, more flexibility. :)

I think there's already an iterator1 variable (or a similar name) containing the name of the index if used.

To clarify, it will be something like.

<div x-data="{ items: [ {tags: ['foo', 'bar']}, {tags: ['baz']} ] }">
  <template x-for="(item, parentIndex) in items">
    <div>
      <p>Tags <span x-text="parentIndex"></span></p>
      <template x-for="(tag, childIndex) in item.tags">
        <span x-text="tag"></span>
        <span x-text="childIndex"></span>
      </template>
    </div>
  </template>
</div>

Can you see any issues with this approach?

All 8 comments

Hi @tmalaher

That is just the standard signature of a javascript forEach loop.
It's used to build the elements but it's not what is passed to each element scope.

If you look at line 40 and 64 of for.js, you'll notice that the only additional variable passed to an element is the current array item (var i) using the element name (var single).
Passing the key (maybe a magic property like $index) could be a good idea for a PR though, if you'd like to have a go. The value is already available in currentKey.

Your example would then become:

<ul>
<template x-for='value in toggles'>
    <li>Index:<span x-text='$index'></span> Value: <span x-text='value'></span></li>
</template>
</ul>

What you refer as collection in the initial example is simply the toggles variable.

P.s. Obviously you should check with @calebporzio if it could be something he wants to add to Alpine but, even if it won't make the core, it's a good exercise to understand how the framework work.

Yeah, I would expect the same actually (index and coll to be available) I realize I didn't add them to the scope of the iterations.

Something I would be willing to pull in as a PR, or eventually implement myself.

Good call.

Hi @SimoTod
I just noticed something on using the $index approach. As far as I understand now, this won't work with nested x-for because they will have the same magic variable names. Something like this.

<div x-data="{ items:[ {tags: ['foo','bar']}, {tags: ['baz']} ] }">
  <template x-for="item in items">
    <div>
      <p>Tags <span x-text="$index"></span></p>
      <template x-for="tag in item.tags">
        <span x-text="tag"></span>
        <!-- which loop this $index belong to?  -->
        <!-- And what if I needed the parent $index inside of here 馃 -->
        <span x-text="$index"></span>
      </template>
    </div>
  </template>
</div>

And I have some feeling that there is a hacky way to deal with this. And am not sure if we can have a workarround for this if we are serious about supporting nested x-for loops.

The magic variable was an initial suggestion but it's actually better to use the same syntax as VueJs (x-for="(value, index) in items").

Less magic, more flexibility. :)

I think there's already an iterator1 variable (or a similar name) containing the name of the index if used.

To clarify, it will be something like.

<div x-data="{ items: [ {tags: ['foo', 'bar']}, {tags: ['baz']} ] }">
  <template x-for="(item, parentIndex) in items">
    <div>
      <p>Tags <span x-text="parentIndex"></span></p>
      <template x-for="(tag, childIndex) in item.tags">
        <span x-text="tag"></span>
        <span x-text="childIndex"></span>
      </template>
    </div>
  </template>
</div>

Can you see any issues with this approach?

Less magic, more flexibility. :)

It doesn't make sense but, it is true :laughing:

Can you see any issue with this approach?

Nope. It's perfect! :)

Hi guys, doesn't seem like there's any further problems here so I'm going to close this issue. Thanks @SimoTod for providing functioning examples :)

is there a PR associated with this resolution?

@clockelliptic nested x-for's and index variable inside a loop are now supported.
Documentation has also been updated.
Is there anything else you think it should be done?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

BernhardBaumrock picture BernhardBaumrock  路  3Comments

bep picture bep  路  4Comments

dstpierre picture dstpierre  路  4Comments

allmarkedup picture allmarkedup  路  4Comments

piotrpog picture piotrpog  路  3Comments