Просмотр исходного кода

Merge pull request #120 from alpinejs/cp/add-dispatch-magic-function

Add `$dispatch()` function
Caleb Porzio 5 лет назад
Родитель
Сommit
31e4b90d3b
8 измененных файлов с 62 добавлено и 12 удалено
  1. 0 0
      dist/alpine.js
  2. 0 0
      dist/alpine.js.map
  3. 11 0
      examples/index.html
  4. 23 8
      src/component.js
  5. 1 1
      src/directives/bind.js
  6. 2 2
      src/directives/for.js
  7. 1 1
      src/directives/on.js
  8. 24 0
      test/dispatch.spec.js

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
dist/alpine.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
dist/alpine.js.map


+ 11 - 0
examples/index.html

@@ -328,6 +328,17 @@
                     </td>
                 </tr>
 
+                <tr>
+                    <td>Dispatch</td>
+                    <td>
+                        <div x-data="{ foo: 'bar' }" x-on:custom-event="foo = $event.detail.newValue">
+                            <span x-text="foo"></span>
+
+                            <button x-on:click="$dispatch('custom-event', {newValue: 'baz'})">Turn 'bar' to 'baz'</button>
+                        </div>
+                    </td>
+                </tr>
+
                 <tr>
                     <td>Cloak</td>
                     <td>

+ 23 - 8
src/component.js

@@ -202,7 +202,7 @@ export default class Component {
                     break;
 
                 case 'text':
-                    var output = this.evaluateReturnExpression(expression, extraVars);
+                    var output = this.evaluateReturnExpression(el, expression, extraVars);
 
                     // If nested model key is undefined, set the default value to empty string.
                     if (output === undefined && expression.match(/\./).length) {
@@ -213,17 +213,17 @@ export default class Component {
                     break;
 
                 case 'html':
-                    el.innerHTML = this.evaluateReturnExpression(expression, extraVars)
+                    el.innerHTML = this.evaluateReturnExpression(el, expression, extraVars)
                     break;
 
                 case 'show':
-                    var output = this.evaluateReturnExpression(expression, extraVars)
+                    var output = this.evaluateReturnExpression(el, expression, extraVars)
 
                     handleShowDirective(el, output, initialUpdate)
                     break;
 
                 case 'if':
-                    var output = this.evaluateReturnExpression(expression, extraVars)
+                    var output = this.evaluateReturnExpression(el, expression, extraVars)
 
                     handleIfDirective(el, output, initialUpdate)
                     break;
@@ -242,12 +242,27 @@ export default class Component {
         })
     }
 
-    evaluateReturnExpression(expression, extraVars = () => {}) {
-        return saferEval(expression, this.$data, extraVars())
+    evaluateReturnExpression(el, expression, extraVars = () => {}) {
+        return saferEval(expression, this.$data, {
+            ...extraVars(),
+            $dispatch: this.getDispatchFunction(el),
+        })
     }
 
-    evaluateCommandExpression(expression, extraVars = () => {}) {
-        saferEvalNoReturn(expression, this.$data, extraVars())
+    evaluateCommandExpression(el, expression, extraVars = () => {}) {
+        saferEvalNoReturn(expression, this.$data, {
+            ...extraVars(),
+            $dispatch: this.getDispatchFunction(el),
+        })
+    }
+
+    getDispatchFunction (el) {
+        return (event, detail = {}) => {
+            el.dispatchEvent(new CustomEvent(event, {
+                detail,
+                bubbles: true,
+            }))
+        }
     }
 
     listenForNewElementsToInitialize() {

+ 1 - 1
src/directives/bind.js

@@ -1,7 +1,7 @@
 import { arrayUnique } from '../utils'
 
 export function handleAttributeBindingDirective(component, el, attrName, expression, extraVars) {
-    var value = component.evaluateReturnExpression(expression, extraVars)
+    var value = component.evaluateReturnExpression(el, expression, extraVars)
 
     if (attrName === 'value') {
         // If nested model key is undefined, set the default value to empty string.

+ 2 - 2
src/directives/for.js

@@ -3,7 +3,7 @@ import { transitionIn, transitionOut, getXAttrs } from '../utils'
 export function handleForDirective(component, el, expression, initialUpdate) {
     const { single, bunch, iterator1, iterator2 } = parseFor(expression)
 
-    var items = component.evaluateReturnExpression(bunch)
+    var items = component.evaluateReturnExpression(el, bunch)
 
     // As we walk the array, we'll also walk the DOM (updating/creating as we go).
     var previousEl = el
@@ -118,6 +118,6 @@ function getThisIterationsKeyFromTemplateTag(component, el, single, iterator1, i
     if (iterator2) keyAliases[iterator2] = group
 
     return keyAttr
-        ? component.evaluateReturnExpression(keyAttr.expression, () => keyAliases)
+        ? component.evaluateReturnExpression(el, keyAttr.expression, () => keyAliases)
         : index
 }

+ 1 - 1
src/directives/on.js

@@ -46,7 +46,7 @@ export function registerListener(component, el, event, modifiers, expression, ex
 }
 
 function runListenerHandler(component, expression, e, extraVars) {
-    component.evaluateCommandExpression(expression, () => {
+    component.evaluateCommandExpression(e.target, expression, () => {
         return {...extraVars(), '$event': e}
     })
 }

+ 24 - 0
test/dispatch.spec.js

@@ -0,0 +1,24 @@
+import Alpine from 'alpinejs'
+import { wait } from '@testing-library/dom'
+
+global.MutationObserver = class {
+    observe() {}
+}
+
+test('$dispatch', async () => {
+    document.body.innerHTML = `
+        <div x-data="{ foo: 'bar' }" x-on:custom-event="foo = $event.detail.newValue">
+            <span x-text="foo"></span>
+
+            <button x-on:click="$dispatch('custom-event', {newValue: 'baz'})"></button>
+        </div>
+    `
+
+    Alpine.start()
+
+    expect(document.querySelector('span').innerText).toEqual('bar')
+
+    document.querySelector('button').click()
+
+    await wait(() => { expect(document.querySelector('span').innerText).toEqual('baz') })
+})

Некоторые файлы не были показаны из-за большого количества измененных файлов