Browse Source

Remove x-model input event listener if x-modelable is used (#2774)

* Revert "Cleanup"

This reverts commit 681e8ebb8914285a9490c31240337424a3549a06.

* Revert "Add named x-modelables"

This reverts commit 91ebbc3963c311f88b988d1faa056f5eb7b34cf0.

* Remove named modelables docs

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

* Remove test
Caleb Porzio 3 năm trước cách đây
mục cha
commit
e262d4ffa1

+ 6 - 15
packages/alpinejs/src/directives/x-model.js

@@ -4,7 +4,7 @@ import { mutateDom } from '../mutation'
 import bind from '../utils/bind'
 import bind from '../utils/bind'
 import on from '../utils/on'
 import on from '../utils/on'
 
 
-directive('model', (el, { value, modifiers, expression }, { effect, cleanup }) => {
+directive('model', (el, { modifiers, expression }, { effect, cleanup }) => {
     let evaluate = evaluateLater(el, expression)
     let evaluate = evaluateLater(el, expression)
     let assignmentExpression = `${expression} = rightSideOfExpression($event, ${expression})`
     let assignmentExpression = `${expression} = rightSideOfExpression($event, ${expression})`
     let evaluateAssignment = evaluateLater(el, assignmentExpression)
     let evaluateAssignment = evaluateLater(el, assignmentExpression)
@@ -27,16 +27,16 @@ directive('model', (el, { value, modifiers, expression }, { effect, cleanup }) =
 
 
     // Register the listener removal callback on the element, so that
     // Register the listener removal callback on the element, so that
     // in addition to the cleanup function, x-modelable may call it.
     // 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 = {}
     if (! el._x_removeModelListeners) el._x_removeModelListeners = {}
+    el._x_removeModelListeners['default'] = removeListener
 
 
-    el._x_removeModelListeners[value || 'default'] = removeListener
-    
-    cleanup(() => el._x_removeModelListeners[value || 'default']())
+    cleanup(() => el._x_removeModelListeners['default']())
 
 
     // Allow programmatic overiding of x-model.
     // Allow programmatic overiding of x-model.
     let evaluateSetModel = evaluateLater(el, `${expression} = __placeholder`)
     let evaluateSetModel = evaluateLater(el, `${expression} = __placeholder`)
-
-    let modelObject = {
+    el._x_model = {
         get() { 
         get() { 
             let result
             let result
             evaluate(value => result = value)
             evaluate(value => result = value)
@@ -47,15 +47,6 @@ directive('model', (el, { value, modifiers, expression }, { effect, cleanup }) =
         },
         },
     }
     }
 
 
-    if (value) {
-        // This is a "named" binding (x-model:name).
-        if (! el._x_models) el._x_models = {}
-        el._x_models[value] = modelObject
-    } else {
-        // This is a "normal" binding (x-model).
-        el._x_model = modelObject
-    }
-
     el._x_forceModelUpdate = () => {
     el._x_forceModelUpdate = () => {
         evaluate(value => {
         evaluate(value => {
             // If nested model key is undefined, set the default value to empty string.
             // If nested model key is undefined, set the default value to empty string.

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

@@ -1,7 +1,6 @@
-import { evaluateLater } from '../evaluator'
 import { directive } from '../directives'
 import { directive } from '../directives'
 
 
-directive('modelable', (el, { value, expression }, { effect, evaluate, evaluateLater }) => {
+directive('modelable', (el, { expression }, { effect, evaluateLater }) => {
     let func = evaluateLater(expression)
     let func = evaluateLater(expression)
     let innerGet = () => { let result; func(i => result = i); return result; }
     let innerGet = () => { let result; func(i => result = i); return result; }
     let evaluateInnerSet = evaluateLater(`${expression} = __placeholder`)
     let evaluateInnerSet = evaluateLater(`${expression} = __placeholder`)
@@ -9,22 +8,19 @@ directive('modelable', (el, { value, expression }, { effect, evaluate, evaluateL
 
 
     let initialValue = innerGet()
     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)
     innerSet(initialValue)
 
 
     queueMicrotask(() => {
     queueMicrotask(() => {
-        if (! el._x_model && ! el._x_models) return
+        if (! el._x_model) return
 
 
         // Remove native event listeners as these are now bound with x-modelable.
         // 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
         // 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
         // in x-modelable/model, but the input events from the native input
         // override any functionality added by x-modelable causing confusion.
         // override any functionality added by x-modelable causing confusion.
-        el._x_removeModelListeners[value || 'default']()
+        el._x_removeModelListeners['default']()
     
     
-        let outerGet = value ? el._x_models[value].get : el._x_model.get
-        let outerSet = value ? el._x_models[value].set : el._x_model.set
+        let outerGet = el._x_model.get
+        let outerSet = el._x_model.set
     
     
         effect(() => innerSet(outerGet()))
         effect(() => innerSet(outerGet()))
         effect(() => outerSet(innerGet()))
         effect(() => outerSet(innerGet()))

+ 0 - 45
packages/docs/src/en/directives/modelable.md

@@ -34,48 +34,3 @@ Here's a simple example of using `x-modelable` to expose a variable for binding
 As you can see the outer scope property "number" is now bound to the inner scope property "count".
 As you can see the outer scope property "number" is now bound to the inner scope property "count".
 
 
 Typically this feature would be used in conjunction with a backend templating framework like Laravel Blade. It's useful for abstracting away Alpine components into backend templates and exposing state to the outside through `x-model` as if it were a native input.
 Typically this feature would be used in conjunction with a backend templating framework like Laravel Blade. It's useful for abstracting away Alpine components into backend templates and exposing state to the outside through `x-model` as if it were a native input.
-
-<a name="named-modelables"></a>
-## Named modelables
-
-Sometimes you may want to expose multiple properties to `x-model`, in these cases you can specify a name in the `x-modelable` and `x-model` directives as a way of degnating additional data bindings.
-
-Here's the example from before, but we'll add another property that we want bound: the "max" value the counter can count up to:
-
-```alpine
-<div x-data="{ number: 5, limit: 10 }">
-    <div
-        x-data="{ count: 0, max: null }"
-        x-modelable="count"
-        x-model="number"
-        x-modelable:max="max"
-        x-model:max="limit"
-    >
-        <button @click="count = count === max ? count : count + 1">
-            Increment
-        </button>
-    </div>
-
-    Number: <span x-text="number"></span>
-</div>
-```
-
-<!-- START_VERBATIM -->
-<div class="demo">
-    <div x-data="{ number: 5, limit: 10 }">
-        <div
-            x-data="{ count: 0, max: null }"
-            x-modelable="count"
-            x-modelable:max="max"
-            x-model="number"
-            x-model:max="limit"
-        >
-            <button @click="count = count === max ? count : count + 1">
-                Increment
-            </button>
-        </div>
-
-        Number: <span x-text="number"></span>
-    </div>
-</div>
-<!-- END_VERBATIM -->

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

@@ -24,21 +24,6 @@ test('can expose data for x-model binding',
     }
     }
 )
 )
 
 
-test('Something like Livewire can hook into x-modelable',
-    html`
-        <h1 x-data="{ value: 'bar' }" x-modelable="value" x-init="
-            () => {}; $el._x_modelable_hook = (val) => {
-                return val.toUpperCase()
-            }
-        ">
-            <span x-text="value"></span>
-        </h1>
-    `,
-    ({ get }) => {
-        get('span').should(haveText('BAR'))
-    }
-)
-
 test('x-modelable works when inside x-bind and x-model is outside',
 test('x-modelable works when inside x-bind and x-model is outside',
     html`
     html`
         <div x-data="{ outer: 'foo', thing: {
         <div x-data="{ outer: 'foo', thing: {
@@ -71,7 +56,6 @@ test('x-modelable removes the event listener used by corresponding x-model',
             <div x-data="{ inner: 'bar' }" x-modelable="inner" x-model="outer">
             <div x-data="{ inner: 'bar' }" x-modelable="inner" x-model="outer">
                 <h1 x-text="outer"></h1>
                 <h1 x-text="outer"></h1>
                 <h2 x-text="inner"></h2>
                 <h2 x-text="inner"></h2>
-
                 <button id="1" @click="$dispatch('input', 'baz')"></button>
                 <button id="1" @click="$dispatch('input', 'baz')"></button>
             </div>
             </div>
         </div>
         </div>
@@ -84,27 +68,3 @@ test('x-modelable removes the event listener used by corresponding x-model',
         get('h2').should(haveText('foo'))
         get('h2').should(haveText('foo'))
     }
     }
 )
 )
-
-test('can have a named x-modelable',
-    html`
-        <div x-data="{ outer: 'foo' }">
-            <div x-data="{ inner: 'bar' }" x-modelable:custom="inner" x-model:custom="outer">
-                <h1 x-text="outer"></h1>
-                <h2 x-text="inner"></h2>
-
-                <button @click="inner = 'bob'" id="1">change inner</button>
-                <button @click="outer = 'lob'" id="2">change outer</button>
-            </div>
-        </div>
-    `,
-    ({ get }) => {
-        get('h1').should(haveText('foo'))
-        get('h2').should(haveText('foo'))
-        get('#1').click()
-        get('h1').should(haveText('bob'))
-        get('h2').should(haveText('bob'))
-        get('#2').click()
-        get('h1').should(haveText('lob'))
-        get('h2').should(haveText('lob'))
-    }
-)