Browse Source

Fix morphing root level state (#4169)

* Fix morph when x-for is used inside x-teleport

* Fix morphing teleports with root-level `x-data` state

* wip
Caleb Porzio 1 year ago
parent
commit
54c7eb70bc

+ 4 - 2
packages/alpinejs/src/directives/x-teleport.js

@@ -49,9 +49,11 @@ directive('teleport', (el, { modifiers, expression }, { cleanup }) => {
     mutateDom(() => {
         placeInDom(clone, target, modifiers)
 
-        skipDuringClone(() => initTree(clone))()
+        skipDuringClone(() => {
+            initTree(clone)
 
-        clone._x_ignore = true
+            clone._x_ignore = true
+        })()
     })
 
     el._x_teleportPutBack = () => {

+ 4 - 5
packages/morph/src/morph.js

@@ -40,6 +40,10 @@ export function morph(from, toHtml, options) {
         // Initialize the server-side HTML element with Alpine...
         if (from.nodeType === 1 && window.Alpine) {
             window.Alpine.cloneNode(from, to)
+
+            if (from._x_teleport && to._x_teleport) {
+                patch(from._x_teleport, to._x_teleport)
+            }
         }
 
         if (textOrComment(to)) {
@@ -120,11 +124,6 @@ export function morph(from, toHtml, options) {
     }
 
     function patchChildren(from, to) {
-        // If we hit a <template x-teleport="body">,
-        // let's use the teleported nodes for this patch...
-        if (from._x_teleport) from = from._x_teleport
-        if (to._x_teleport) to = to._x_teleport
-
         let fromKeys = keyToMap(from.children)
         let fromKeyHoldovers = {}
 

+ 74 - 0
tests/cypress/integration/plugins/morph.spec.js

@@ -134,6 +134,51 @@ test('can morph teleports',
     },
 )
 
+test('can morph teleports in different places with IDs',
+    [html`
+        <div x-data="{ count: 1 }" id="a">
+            <button @click="count++">Inc</button>
+
+            <template x-teleport="#b" id="template">
+                <div>
+                    <h1 x-text="count"></h1>
+                    <h2>hey</h2>
+                </div>
+            </template>
+
+            <div>moving placeholder</div>
+        </div>
+
+        <div id="b"></div>
+    `],
+    ({ get }, reload, window, document) => {
+        let toHtml = html`
+        <div x-data="{ count: 1 }" id="a">
+            <button @click="count++">Inc</button>
+
+            <div>moving placeholder</div>
+
+            <template x-teleport="#b" id="template">
+                <div>
+                    <h1 x-text="count"></h1>
+                    <h2>there</h2>
+                </div>
+            </template>
+        </div>
+        `
+        get('h1').should(haveText('1'))
+        get('h2').should(haveText('hey'))
+        get('button').click()
+        get('h1').should(haveText('2'))
+        get('h2').should(haveText('hey'))
+
+        get('div#a').then(([el]) => window.Alpine.morph(el, toHtml))
+
+        get('h1').should(haveText('2'))
+        get('h2').should(haveText('there'))
+    },
+)
+
 test('can morph',
     [html`
         <ul>
@@ -578,3 +623,32 @@ test('can morph teleports with x-for',
         get('button').should(haveText('3'));
     },
 )
+
+test('can morph teleports with root-level state',
+    [html`
+    <main x-data>
+        <template x-teleport="body">
+            <div x-data="{ foo: 'bar' }">
+                <h1 x-text="foo"></h1>
+            </div>
+        </template>
+    </main>
+    `],
+    ({ get }, reload, window, document) => {
+        let toHtml = html`
+        <main x-data>
+            <template x-teleport="body">
+                <div x-data="{ foo: 'bar' }">
+                    <h1 x-text="foo"></h1>
+                </div>
+            </template>
+        </main>
+        `
+
+        get('h1').should(haveText('bar'));
+
+        get('main').then(([el]) => window.Alpine.morph(el, toHtml));
+
+        get('h1').should(haveText('bar'));
+    },
+)