Ver Fonte

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 há 3 anos atrás
pai
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 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 assignmentExpression = `${expression} = rightSideOfExpression($event, ${expression})`
     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
     // 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
 
-    el._x_removeModelListeners[value || 'default'] = removeListener
-    
-    cleanup(() => el._x_removeModelListeners[value || 'default']())
+    cleanup(() => el._x_removeModelListeners['default']())
 
     // Allow programmatic overiding of x-model.
     let evaluateSetModel = evaluateLater(el, `${expression} = __placeholder`)
-
-    let modelObject = {
+    el._x_model = {
         get() { 
             let result
             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 = () => {
         evaluate(value => {
             // 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'
 
-directive('modelable', (el, { value, 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,22 +8,19 @@ directive('modelable', (el, { value, expression }, { effect, evaluate, evaluateL
 
     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 && ! el._x_models) return
+        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[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(() => 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".
 
 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',
     html`
         <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">
                 <h1 x-text="outer"></h1>
                 <h2 x-text="inner"></h2>
-
                 <button id="1" @click="$dispatch('input', 'baz')"></button>
             </div>
         </div>
@@ -84,27 +68,3 @@ test('x-modelable removes the event listener used by corresponding x-model',
         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'))
-    }
-)