Browse Source

Add $nextTick magic function

Caleb Porzio 5 năm trước cách đây
mục cha
commit
5757842927
6 tập tin đã thay đổi với 97 bổ sung4 xóa
  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.

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
dist/alpine.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 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') })
+})

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác