Jelajahi Sumber

fix x-id when used with morph

Caleb Porzio 1 tahun lalu
induk
melakukan
9c3d5272f8

+ 12 - 1
packages/alpinejs/src/directives/x-id.js

@@ -1,8 +1,19 @@
+import { interceptClone } from "../clone"
 import { directive } from "../directives"
 import { setIdRoot } from '../ids'
 
 directive('id', (el, { expression }, { evaluate }) => {
     let names = evaluate(expression)
-    
+
     names.forEach(name => setIdRoot(el, name))
 })
+
+interceptClone((from, to) => {
+    // Transfer over existing ID registrations from
+    // the existing dom tree over to the new one
+    // so that there aren't ID mismatches...
+    if (from._x_ids) {
+        to._x_ids = from._x_ids
+    }
+})
+

+ 23 - 2
packages/alpinejs/src/magics/$id.js

@@ -1,14 +1,35 @@
 import { magic } from '../magics'
 import { closestIdRoot, findAndIncrementId } from '../ids'
+import { interceptClone } from '../clone'
+
+magic('id', (el, { cleanup }) => (name, key = null) => {
+    // We only want $id to run once per an element's lifecycle...
+    if (el._x_id) return el._x_id
 
-magic('id', el => (name, key = null) => {
     let root = closestIdRoot(el, name)
 
     let id = root
         ? root._x_ids[name]
         : findAndIncrementId(name)
 
-    return key
+    let output = key
         ? `${name}-${id}-${key}`
         : `${name}-${id}`
+
+    el._x_id = output
+
+    cleanup(() => {
+        delete el._x_id
+    })
+
+    return output
+})
+
+interceptClone((from, to) => {
+    // Transfer over existing ID registrations from
+    // the existing dom tree over to the new one
+    // so that there aren't ID mismatches...
+    if (from._x_id) {
+        to._x_id = from._x_id
+    }
 })

+ 32 - 1
tests/cypress/integration/magics/$id.spec.js

@@ -103,7 +103,7 @@ test('$id scopes can be reset',
             <div x-data>
                 <h1 :id="$id('foo')"></h1>
                 <h5 :id="$id('bar')"></h5>
-                
+
                 <div x-id="['foo']">
                     <h2 :aria-labelledby="$id('foo')"></h2>
                     <h6 :aria-labelledby="$id('bar')"></h6>
@@ -127,3 +127,34 @@ test('$id scopes can be reset',
         get('h6').should(haveAttribute('aria-labelledby', 'bar-1'))
     }
 )
+
+test('can be used with morph without losing track',
+    [html`
+        <div x-data>
+            <p x-id="['foo']">
+                <span :id="$id('foo')">bob</span>
+            </p>
+
+            <h1 :id="$id('bar')">lob</h1>
+        </div>
+    `],
+    ({ get }, reload, window, document) => {
+        let toHtml = html`
+            <div x-data>
+                <p x-id="['foo']">
+                    <span :id="$id('foo')">bob</span>
+                </p>
+
+                <h1 :id="$id('bar')">lob</h1>
+            </div>
+        `
+
+        get('span').should(haveAttribute('id', 'foo-1'))
+        get('h1').should(haveAttribute('id', 'bar-1'))
+
+        get('div').then(([el]) => window.Alpine.morph(el, toHtml))
+
+        get('span').should(haveAttribute('id', 'foo-1'))
+        get('h1').should(haveAttribute('id', 'bar-1'))
+    },
+)