Selaa lähdekoodia

add test and crappy implementation for eagerly evaluating x-model when $model is inside x-data

Caleb Porzio 1 vuosi sitten
vanhempi
commit
46677ef230

+ 10 - 6
index.html

@@ -10,11 +10,15 @@
     <!-- <script src="//cdn.tailwindcss.com"></script> -->
     <!-- <script src="//cdn.tailwindcss.com"></script> -->
 
-    <div x-data="{ val: true }"
-    >
-   <input type="text" x-model.boolean="val">
-   <input type="checkbox" x-model.boolean="val">
-   <input type="radio" name="foo" value="true" x-model.boolean="val">
-   <input type="radio" name="foo" value="false" x-model.boolean="val">
+    <div x-data="{ count: 1 }">
+        <div>
+            <span x-text="count"></span>
+
+            <main x-data="{ value: $model }" x-model="count">
+                Count: <h1 x-text="value"></h1>
+
+                <input type="text" x-model="value">
+            </main>
+        </div>
     </div>
 </html>

+ 6 - 1
packages/alpinejs/src/directives/x-data.js

@@ -7,6 +7,7 @@ import { addScopeToNode } from '../scope'
 import { injectMagics, magic } from '../magics'
 import { reactive } from '../reactivity'
 import { evaluate } from '../evaluator'
+import { eagerlyRunXModelIfMagicModelIsUsedInsideThisExpression } from '../magics/$model'
 
 addRootSelector(() => `[${prefix('data')}]`)
 
@@ -21,7 +22,11 @@ directive('data', ((el, { expression }, { cleanup }) => {
     let dataProviderContext = {}
     injectDataProviders(dataProviderContext, magicContext)
 
-    let data = evaluate(el, expression, { scope: dataProviderContext })
+    let data
+
+    eagerlyRunXModelIfMagicModelIsUsedInsideThisExpression(() => {
+        data = evaluate(el, expression, { scope: dataProviderContext })
+    })
 
     if (data === undefined || data === true) data = {}
 

+ 4 - 0
packages/alpinejs/src/directives/x-model.js

@@ -8,6 +8,10 @@ import { warn } from '../utils/warn'
 import { isCloning } from '../clone'
 
 directive('model', (el, { modifiers, expression }, { effect, cleanup }) => {
+    // Don't register x-model if it's already been registered on this element.
+    // This happens when $model eagerly evaluates x-model inside x-data...
+    if (el._x_model) return
+
     let scopeTarget = el
 
     if (modifiers.includes('parent')) scopeTarget = el.parentNode

+ 25 - 0
packages/alpinejs/src/magics/$model.js

@@ -1,9 +1,12 @@
+import { directives } from '../directives'
 import { accessor } from '../interceptor'
 import { findClosest } from '../lifecycle'
 import { magic } from '../magics'
 import { reactive } from '../reactivity'
 
 magic('model', (el, { cleanup }) => {
+    eagerlyRunXModelIfNeeded(el)
+
     let func = generateModelAccessor(el, cleanup)
 
     Object.defineProperty(func, 'closest', { get() {
@@ -59,3 +62,25 @@ function generateModelAccessor(el, cleanup) {
 
     return accessor
 }
+
+let isInsideXData = false
+
+export function eagerlyRunXModelIfMagicModelIsUsedInsideThisExpression(callback) {
+    isInsideXData = true
+
+    callback()
+
+    isInsideXData = false
+}
+
+function eagerlyRunXModelIfNeeded(el) {
+    if (! isInsideXData) return
+
+    if (! el.hasAttribute('x-model')) return
+
+    let attribute = { name: 'x-model', value: el.getAttribute('x-model') }
+
+    directives(el, [attribute]).forEach((handle) => {
+        handle()
+    })
+}

+ 3 - 26
tests/cypress/integration/magics/$model.spec.js

@@ -89,39 +89,16 @@ test('$model can be used with another x-model',
     }
 )
 
-test('$model can be used on the same element as the corresponding x-model',
-    [html`
+test.only('$model can be used on the same element as the corresponding x-model',
+    html`
         <div x-data="{ foo: 'bar' }">
             <button @click="foo = 'baz'">click me</button>
 
-            <div x-test x-model="foo">
+            <div x-data="{ value: $model }" x-model="foo">
                 <h1 x-text="value"></h1>
             </div>
         </div>
     `,
-    `
-        Alpine.directive('test', el => {
-            Alpine.bind(el, {
-                'x-data'() {
-                    return {
-                        internalValue: 'bob',
-                        get value() {
-                            if (this.$model) return this.$model.get()
-
-                            return this.internalValue
-                        },
-                        set value(value) {
-                            if (this.$model) {
-                                this.$model.set(value)
-                            } else {
-                                this.internalValue = value
-                            }
-                        }
-                    }
-                }
-            })
-        })
-    `],
     ({ get }) => {
         get('h1').should(haveText('bar'))
         get('button').click()