alpinejs / alpinejs/alpine

x-for reorders nested templates incorrectly

Open
#4,911 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
HTML
Stars
31.9k
Forks
1.4k
Avg merge
2d 14h
Merged PRs (30d)
17

Description

Alpine.js version

3.17.3

Browser and operating system

Chrome, Firefox on Linux

Describe the issue you're experiencing

I found a problem with reordering items in x-for when its direct child is another <template>. It happens with nested x-for and also with nested x-if, even when :key is set.

Clicking Reverse changes the array, but the displayed items end up in the wrong order.

It looks like Alpine moves the template nodes, but not all the elements rendered by the nested templates.

I have a fix for this and will open a PR shortly.

This goes back to Alpine 3.0.0. I tested 22 releases, including the latest patch of each minor version from 3.0 to 3.17. Both examples fail in all tested versions, though the behavior changes:

  • In early versions, no content is rendered.
  • In later versions, the content appears but the initial order is wrong.
  • From 3.15.9 onward, the tested versions render correctly at first, but reverse() breaks the order.

I didn't test every intermediate patch release.

Code snippets to reproduce the issue
<!doctype html>
<html>
<body>
    <div x-data="{
        groups: [
            { key: 'a', values: ['aa', 'ab'] },
            { key: 'b', values: ['ba', 'bb'] },
            { key: 'c', values: ['ca', 'cb'] },
        ]
    }">
        <button @click="groups.reverse()">Reverse</button>

        <template x-for="group in groups" :key="group.key">
            <template x-for="value in group.values" :key="value">
                <div x-text="value"></div>
            </template>
        </template>
    </div>

    <hr>

    <div x-data="{ items: [1, 2, 3] }">
        <button @click="items.reverse()">Reverse</button>

        <template x-for="n in items" :key="n">
            <template x-if="true">
                <template x-if="true">
                    <span x-text="n"></span>
                </template>
            </template>
        </template>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/alpinejs@3.17.3/dist/cdn.min.js"></script>
</body>
</html>

Actual results after one click on each button:

  • First example: aa, ba, bb, ab, ca, cb
  • Second example: 2, 1, 3
Screenshots/screen recordings

No response

How do you expect it to work?

The displayed items should follow the new array order.

In the first example, the groups should be reversed, with each group's children moving together.
Expected order: ca, cb, ba, bb, aa, ab.

In the second example, the elements rendered by the nested x-if templates should move with their items.
Expected order: 3, 2, 1.

Please confirm (incomplete submissions will not be addressed)
  • I have provided easy and step-by-step instructions to reproduce the bug.
  • I have provided code samples as text and NOT images.
  • I understand my bug report will be removed if I haven't met the criteria above.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the two nested-template HTML reproductions in the issue and click each Reverse button to compare the actual and expected orders. Trace Alpine's handling of nested x-for and x-if templates, then verify that each rendered group or item moves together and both examples produce the documented expected order.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
frontend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.