Просмотр исходного кода

Wrong order when x-for children use x-if (#2421)

* Add failing test

* Fix x-for rendered at reverse when combined with x-if

* Add additional tests for reordering children using x-if

* Make sure x-if generated nodes follow the parent template when x-for is reordered
Simone Todaro 3 лет назад
Родитель
Сommit
a78cf8e52f

+ 5 - 0
packages/alpinejs/src/directives/x-for.js

@@ -154,7 +154,9 @@ function loop(el, iteratorNames, evaluateItems, evaluateKey) {
             mutateDom(() => {
                 elForSpot.after(marker)
                 elInSpot.after(elForSpot)
+                elForSpot._x_currentIfEl && elForSpot.after(elForSpot._x_currentIfEl)
                 marker.before(elInSpot)
+                elInSpot._x_currentIfEl && elInSpot.after(elInSpot._x_currentIfEl)
                 marker.remove()
             })
 
@@ -166,6 +168,9 @@ function loop(el, iteratorNames, evaluateItems, evaluateKey) {
             let [lastKey, index] = adds[i]
 
             let lastEl = (lastKey === 'template') ? templateEl : lookup[lastKey]
+            // If the element is a x-if template evaluated to true,
+            // point lastEl to the if-generated node
+            if (lastEl._x_currentIfEl) lastEl = lastEl._x_currentIfEl
 
             let scope = scopes[index]
             let key = keys[index]

+ 48 - 0
tests/cypress/integration/directives/x-for.spec.js

@@ -476,3 +476,51 @@ test('x-for works with variables that start with const',
         get('li:nth-of-type(3)').should(haveText('c'))
     }
 )
+
+test('renders children in the right order when combined with x-if',
+    html`
+        <div x-data="{ items: ['foo', 'bar'] }">
+            <template x-for="item in items">
+                <template x-if="true">
+                    <span x-text="item"></span>
+                </template>
+            </template>
+        </div>
+    `,
+    ({ get }) => {
+        get('span:nth-of-type(1)').should(haveText('foo'))
+        get('span:nth-of-type(2)').should(haveText('bar'))
+    }
+)
+
+test('correctly renders x-if children when reordered',
+    html`
+        <div x-data="{ items: ['foo', 'bar'] }">
+            <button @click="items = ['bar', 'foo']">click me</button>
+            <button @click="items = ['bar', 'baz', 'foo']">click me</button>
+            <button @click="items = ['baz', 'foo']">click me</button>
+            <template x-for="item in items" :key="item">
+                <template x-if="true">
+                    <span x-text="item"></span>
+                </template>
+            </template>
+        </div>
+    `,
+    ({ get }) => {
+        get('span:nth-of-type(1)').should(haveText('foo'))
+        get('span:nth-of-type(2)').should(haveText('bar'))
+        get('button:nth-of-type(1)').click()
+        get('span').should(haveLength('2'))
+        get('span:nth-of-type(1)').should(haveText('bar'))
+        get('span:nth-of-type(2)').should(haveText('foo'))
+        get('button:nth-of-type(2)').click()
+        get('span').should(haveLength('3'))
+        get('span:nth-of-type(1)').should(haveText('bar'))
+        get('span:nth-of-type(2)').should(haveText('baz'))
+        get('span:nth-of-type(3)').should(haveText('foo'))
+        get('button:nth-of-type(3)').click()
+        get('span').should(haveLength('2'))
+        get('span:nth-of-type(1)').should(haveText('baz'))
+        get('span:nth-of-type(2)').should(haveText('foo'))
+    }
+)