Переглянути джерело

Add $nextTick magic function

Caleb Porzio 5 роки тому
батько
коміт
5757842927
6 змінених файлів з 97 додано та 4 видалено
  1. 42 3
      README.md
  2. 0 0
      dist/alpine.js
  3. 0 0
      dist/alpine.js.map
  4. 1 1
      package.json
  5. 30 0
      src/component.js
  6. 24 0
      test/next-tick.spec.js

+ 42 - 3
README.md

@@ -8,13 +8,13 @@ You get to keep your DOM, and sprinkle in behavior as you see fit.
 
 Think of it like [Tailwind](https://tailwindcss.com/) for JavaScript.
 
-> Note: This tool's syntax is almost entirely borrowed from [Vue.js](https://vuejs.org/) (and by extension [Angular](https://angularjs.org/)). I am forever grateful for the gift they are to the web.
+> Note: This tool's syntax is almost entirely borrowed from [Vue](https://vuejs.org/) (and by extension [Angular](https://angularjs.org/)). I am forever grateful for the gift they are to the web.
 
 ## Install
 
 **From CDN:** Add the following script to the end of your `<head>` section.
 ```html
-<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v1.3.1/dist/alpine.js" defer></script>
+<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v1.4.0/dist/alpine.js" defer></script>
 ```
 
 That's it. It will initialize itself.
@@ -101,7 +101,14 @@ There are 12 directives available to you:
 | [`x-transition`](#x-transition) |
 | [`x-cloak`](#x-cloak) |
 
-Here's how they each work:
+And 2 magic objects/functions:
+
+| Magic object/function
+| --- |
+| `$refs`(#refs) |
+| `$nextTick`(#next-tick) |
+
+### Directives
 
 ---
 
@@ -354,3 +361,35 @@ These behave exactly like VueJs's transition directives, except they have differ
     [x-cloak] { display: none; }
 </style>
 ```
+
+### Magic Objects/Functions
+
+---
+
+### `$refs`
+**Example:**
+```html
+<span x-ref="foo">
+
+<button x-on:click="$refs.foo.innerText = 'bar'">
+```
+
+`$refs` is a magic object that can be used to retreive DOM elements marked with `x-ref` inside the component. This is useful when you need to manually manipulate DOM elements.
+
+---
+
+### `$nextTick`
+**Example:**
+```html
+<div x-data="{ fruit: 'apple' }">
+    <button
+        x-on:click="
+            fruit = 'pear';
+            $nextTick(() => { console.log($event.target.innerText) });
+        "
+        x-text="fruit"
+    >
+</div>
+```
+
+`$nextTick` is a magic function that allows you to only execute a given expression AFTER Alpine has made it's reactive DOM updates. This is useful for times you want to interact with the DOM state AFTER it's reflected any data updates you've made.

Різницю між файлами не показано, бо вона завелика
+ 0 - 0
dist/alpine.js


Різницю між файлами не показано, бо вона завелика
+ 0 - 0
dist/alpine.js.map


+ 1 - 1
package.json

@@ -1,7 +1,7 @@
 {
   "main": "dist/alpine.js",
   "name": "alpinejs",
-  "version": "1.3.1",
+  "version": "1.4.0",
   "repository": {
     "type": "git",
     "url": "git://github.com/alpinejs/alpine.git"

+ 30 - 0
src/component.js

@@ -4,10 +4,18 @@ export default class Component {
     constructor(el) {
         this.el = el
 
+        // For $nextTick().
+        this.tickStack = []
+        this.collectingTickCallbacks = false
+
         const rawData = saferEval(this.el.getAttribute('x-data'), {})
 
         rawData.$refs =  this.getRefsProxy()
 
+        rawData.$nextTick =  (callback) => {
+            this.delayRunByATick(callback)
+        }
+
         this.runXInit(this.el.getAttribute('x-init'), rawData)
 
         this.data = this.wrapDataInObservable(rawData)
@@ -17,6 +25,25 @@ export default class Component {
         this.listenForNewElementsToInitialize()
     }
 
+    delayRunByATick(callback) {
+        if (this.collectingTickCallbacks) {
+            this.tickStack.push(callback)
+        } else {
+            callback()
+        }
+    }
+
+    startTick() {
+        this.collectingTickCallbacks = true
+    }
+
+    clearAndEndTick() {
+        this.tickStack.forEach(callable => callable())
+        this.tickStack = []
+
+        this.collectingTickCallbacks = false
+    }
+
     runXInit(initExpression, rawData) {
         initExpression && saferEvalNoReturn(initExpression, rawData)
     }
@@ -183,8 +210,11 @@ export default class Component {
             walkSkippingNestedComponents(rootEl, callback)
 
             self.concernedData = []
+            self.clearAndEndTick()
         }
 
+        this.startTick()
+
         debounce(walkThenClearDependancyTracker, 5)(this.el, function (el) {
             getXAttrs(el).forEach(({ type, value, expression }) => {
                 if (! actionByDirectiveType[type]) return

+ 24 - 0
test/next-tick.spec.js

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

Деякі файли не було показано, через те що забагато файлів було змінено