1
0
Эх сурвалжийг харах

Add "remove event listener" functionality if x-modelable is applied to x-model

Caleb Porzio 3 жил өмнө
parent
commit
9de07b07b5

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

@@ -25,7 +25,14 @@ directive('model', (el, { modifiers, expression }, { effect, cleanup }) => {
         }})
     })
 
-    cleanup(() => removeListener())
+    // Register the listener removal callback on the element, so that
+    // in addition to the cleanup function, x-modelable may call it.
+    // Also, make this a keyed object if we decide to reintroduce
+    // "named modelables" some time in a future Alpine version.
+    if (! el._x_removeModelListeners) el._x_removeModelListeners = {}
+    el._x_removeModelListeners['default'] = removeListener
+
+    cleanup(() => el._x_removeModelListeners['default']())
 
     // Allow programmatic overiding of x-model.
     let evaluateSetModel = evaluateLater(el, `${expression} = __placeholder`)

+ 7 - 5
packages/alpinejs/src/directives/x-modelable.js

@@ -1,7 +1,6 @@
-import { evaluateLater } from '../evaluator'
 import { directive } from '../directives'
 
-directive('modelable', (el, { expression }, { effect, evaluate, evaluateLater }) => {
+directive('modelable', (el, { expression }, { effect, evaluateLater }) => {
     let func = evaluateLater(expression)
     let innerGet = () => { let result; func(i => result = i); return result; }
     let evaluateInnerSet = evaluateLater(`${expression} = __placeholder`)
@@ -9,13 +8,16 @@ directive('modelable', (el, { expression }, { effect, evaluate, evaluateLater })
 
     let initialValue = innerGet()
 
-    // Allow packages like Livewire to hook into $modelable. Ex: `wire:model.defer`
-    if (el._x_modelable_hook) initialValue = el._x_modelable_hook(initialValue)
-
     innerSet(initialValue)
 
     queueMicrotask(() => {
         if (! el._x_model) return
+
+        // Remove native event listeners as these are now bound with x-modelable.
+        // The reason for this is that it's often useful to wrap <input> elements
+        // in x-modelable/model, but the input events from the native input
+        // override any functionality added by x-modelable causing confusion.
+        el._x_removeModelListeners['default']()
     
         let outerGet = el._x_model.get
         let outerSet = el._x_model.set

+ 19 - 0
tests/cypress/integration/directives/x-modelable.spec.js

@@ -64,3 +64,22 @@ test('x-modelable works when inside x-bind and x-model is outside',
         get('h2').should(haveText('lob'))
     }
 )
+
+test('x-modelable removes the event listener used by corresponding x-model',
+    html`
+        <div x-data="{ outer: 'foo' }">
+            <div x-data="{ inner: 'bar' }" x-modelable="inner" x-model="outer">
+                <h1 x-text="outer"></h1>
+                <h2 x-text="inner"></h2>
+                <button id="1" @click="$dispatch('input', 'baz')"></button>
+            </div>
+        </div>
+    `,
+    ({ get }) => {
+        get('h1').should(haveText('foo'))
+        get('h2').should(haveText('foo'))
+        get('#1').click()
+        get('h1').should(haveText('foo'))
+        get('h2').should(haveText('foo'))
+    }
+)