Ver código fonte

Fix x-model event listener doubling when cloned (#3393)

* skip x-model during clone, let mutation observer handle it

* added test

* wip

* wip
Austen Cameron 2 anos atrás
pai
commit
47510938d6

+ 1 - 1
packages/alpinejs/src/clone.js

@@ -2,7 +2,7 @@ import { effect, release, overrideEffect } from "./reactivity"
 import { initTree, isRoot } from "./lifecycle"
 import { walk } from "./utils/walk"
 
-let isCloning = false
+export let isCloning = false
 
 export function skipDuringClone(callback, fallback = () => {}) {
     return (...args) => isCloning ? fallback(...args) : callback(...args)

+ 5 - 1
packages/alpinejs/src/directives/x-model.js

@@ -5,6 +5,7 @@ import { nextTick } from '../nextTick'
 import bind from '../utils/bind'
 import on from '../utils/on'
 import { warn } from '../utils/warn'
+import { isCloning } from '../clone'
 
 directive('model', (el, { modifiers, expression }, { effect, cleanup }) => {
     let scopeTarget = el
@@ -62,7 +63,10 @@ directive('model', (el, { modifiers, expression }, { effect, cleanup }) => {
         || modifiers.includes('lazy')
             ? 'change' : 'input'
 
-    let removeListener = on(el, event, modifiers, (e) => {
+    // We only want to register the event listener when we're not cloning, since the
+    // mutation observer handles initializing the x-model directive already when
+    // the element is inserted into the DOM. Otherwise we register it twice.
+    let removeListener = isCloning ? () => {} : on(el, event, modifiers, (e) => {
         setValue(getInputValue(el, modifiers, e, getValue()))
     })
 

+ 31 - 0
tests/cypress/integration/clone.spec.js

@@ -97,3 +97,34 @@ test('wont register listeners on clone',
         get('#copy span').should(haveText('1'))
     }
 )
+
+test('wont register extra listeners on x-model on clone',
+    html`
+        <script>
+            document.addEventListener('alpine:initialized', () => {
+                window.original = document.getElementById('original')
+                window.copy = document.getElementById('copy')
+            })
+        </script>
+
+        <button x-data @click="Alpine.clone(original, copy)">click</button>
+
+        <div x-data="{ checks: [] }" id="original">
+            <input type="checkbox" x-model="checks" value="1">
+            <span x-text="checks"></span>
+        </div>
+
+        <div x-data="{ checks: [] }" id="copy">
+            <input type="checkbox" x-model="checks" value="1">
+            <span x-text="checks"></span>
+        </div>
+    `,
+    ({ get }) => {
+        get('#original span').should(haveText(''))
+        get('#copy span').should(haveText(''))
+        get('button').click()
+        get('#copy span').should(haveText(''))
+        get('#copy input').click()
+        get('#copy span').should(haveText('1'))
+    }
+)