瀏覽代碼

Allow passing runtime parameters to Alpine.data() objects (#1660)

* Allow passing parameters to Alpine.data

* Add docs
Caleb Porzio 3 年之前
父節點
當前提交
577e63f6d2

+ 14 - 2
packages/alpinejs/src/datas.js

@@ -5,6 +5,18 @@ export function data(name, callback) {
     datas[name] = callback
 }
 
-export function getNamedDataProvider(name) {
-    return datas[name]
+export function injectDataProviders(obj, context) {
+    Object.entries(datas).forEach(([name, callback]) => {
+        Object.defineProperty(obj, name, {
+            get() {
+                return (...args) => {
+                    return callback.bind(context)(...args)
+                }
+            },
+
+            enumerable: false,
+        })
+    })
+
+    return obj
 }

+ 7 - 11
packages/alpinejs/src/directives/x-data.js

@@ -1,10 +1,10 @@
 import { directive, prefix } from '../directives'
 import { initInterceptors } from '../interceptor'
-import { getNamedDataProvider } from '../datas'
+import { injectDataProviders } from '../datas'
 import { addRootSelector } from '../lifecycle'
 import { skipDuringClone } from '../clone'
 import { addScopeToNode } from '../scope'
-import { injectMagics } from '../magics'
+import { injectMagics, magic } from '../magics'
 import { reactive } from '../reactivity'
 import { evaluate } from '../evaluator'
 
@@ -13,17 +13,13 @@ addRootSelector(() => `[${prefix('data')}]`)
 directive('data', skipDuringClone((el, { expression }, { cleanup }) => {
     expression = expression === '' ? '{}' : expression
 
-    let dataProvider = getNamedDataProvider(expression)
+    let magicContext = {}
+    injectMagics(magicContext, el)
 
-    let data = {}
+    let dataProviderContext = {}
+    injectDataProviders(dataProviderContext, magicContext)
 
-    if (dataProvider) {
-        let magics = injectMagics({}, el)
-
-        data = dataProvider.bind(magics)()
-    } else {
-        data = evaluate(el, expression)
-    }
+    data = evaluate(el, expression, { scope: dataProviderContext })
 
     injectMagics(data, el)
 

+ 16 - 0
packages/docs/src/en/globals/alpine-data.md

@@ -57,6 +57,22 @@ export default function () => ({
 })
 ```
 
+<a name="initial-parameters"></a>
+## Initial Parameters
+
+In addition to referencing `Alpine.data` providers by their name plainly (like `x-data="dropdown"`), you can also reference them as functions (`x-data="dropdown"`). By calling them as functions directly, you can pass in additional parameters to be used when creating the initial data object like so:
+
+```html
+<div x-data="dropdown(true)">
+```
+```js
+Alpine.data('dropdown', (initialOpenState = false) => ({
+    open: initialOpenState
+}))
+```
+
+Now, you can re-use the `dropdown` object, but provide it with different parameters as you need to.
+
 <a name="init-functions"></a>
 ## Init functions
 

+ 47 - 0
tests/cypress/integration/custom-data.spec.js

@@ -17,6 +17,53 @@ test('can register custom data providers',
     ({ get }) => get('span').should(haveText('bar'))
 )
 
+test.only('can accept initial params',
+    html`
+        <script>
+            document.addEventListener('alpine:init', () => {
+                Alpine.data('test', (first, second) => ({
+                    foo: first,
+                    bar: second,
+                }))
+            })
+        </script>
+
+        <div x-data="test('baz', 'bob')">
+            <h1 x-text="foo"></h1>
+            <h2 x-text="bar"></h2>
+        </div>
+    `,
+    ({ get }) => {
+        get('h1').should(haveText('baz'))
+        get('h2').should(haveText('bob'))
+    }
+)
+
+test.only('can spread together',
+    html`
+        <script>
+            document.addEventListener('alpine:init', () => {
+                Alpine.data('test', (first) => ({
+                    foo: first,
+                }))
+
+                Alpine.data('test2', (second) => ({
+                    bar: second,
+                }))
+            })
+        </script>
+
+        <div x-data="{ ...test('baz'), ...test2('bob') }">
+            <h1 x-text="foo"></h1>
+            <h2 x-text="bar"></h2>
+        </div>
+    `,
+    ({ get }) => {
+        get('h1').should(haveText('baz'))
+        get('h2').should(haveText('bob'))
+    }
+)
+
 test('init functions inside custom datas are called automatically',
     html`
         <script>