浏览代码

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') })
+})

部分文件因为文件数量过多而无法显示