@alpinejs/docs 3.4.2-revision.2 → 3.5.2-revision.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/en/directives/bind.md +9 -1
- package/src/en/directives/cloak.md +1 -1
- package/src/en/directives/data.md +1 -1
- package/src/en/directives/effect.md +1 -1
- package/src/en/directives/for.md +1 -1
- package/src/en/directives/html.md +1 -1
- package/src/en/directives/if.md +1 -1
- package/src/en/directives/ignore.md +1 -1
- package/src/en/directives/init.md +1 -1
- package/src/en/directives/model.md +37 -1
- package/src/en/directives/on.md +16 -1
- package/src/en/directives/ref.md +1 -1
- package/src/en/directives/show.md +1 -1
- package/src/en/directives/text.md +1 -1
- package/src/en/directives/transition.md +3 -3
- package/src/en/essentials/installation.md +1 -1
- package/src/en/essentials/lifecycle.md +2 -2
- package/src/en/magics/data.md +45 -0
- package/src/en/magics/dispatch.md +9 -9
- package/src/en/magics/el.md +1 -1
- package/src/en/magics/nextTick.md +1 -1
- package/src/en/magics/refs.md +1 -1
- package/src/en/magics/root.md +1 -1
- package/src/en/magics/store.md +1 -1
- package/src/en/magics/watch.md +1 -1
- package/src/en/plugins/intersect.md +22 -0
- package/src/en/plugins/morph.md +251 -0
- package/src/en/plugins/persist.md +11 -0
- package/src/en/plugins/trap.md +73 -0
- package/src/en/upgrade-guide.md +6 -21
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@ order: 4
|
|
|
3
3
|
title: bind
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# x-bind
|
|
7
7
|
|
|
8
8
|
`x-bind` allows you to set HTML attributes on elements based on the result of JavaScript expressions.
|
|
9
9
|
|
|
@@ -124,6 +124,14 @@ Just like the class objects, this syntax is entirely optional. Only use it if it
|
|
|
124
124
|
<div style="color: red; display: flex;" ...>
|
|
125
125
|
```
|
|
126
126
|
|
|
127
|
+
Conditional inline styling is possible using expressions just like with x-bind:class. Short circuit operators can be used here as well by using a styles object as the second operand.
|
|
128
|
+
```alpine
|
|
129
|
+
<div x-bind:style="true && { color: 'red' }">
|
|
130
|
+
|
|
131
|
+
<!-- Will render: -->
|
|
132
|
+
<div style="color: red;">
|
|
133
|
+
```
|
|
134
|
+
|
|
127
135
|
One advantage of this approach is being able to mix it in with existing styles on an element:
|
|
128
136
|
|
|
129
137
|
```alpine
|
|
@@ -3,7 +3,7 @@ order: 11
|
|
|
3
3
|
title: effect
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# x-effect
|
|
7
7
|
|
|
8
8
|
`x-effect` is a useful directive for re-evaluating an expression when one of its dependencies change. You can think of it as a watcher where you don't have to specify what property to watch, it will watch all properties used within it.
|
|
9
9
|
|
package/src/en/directives/for.md
CHANGED
package/src/en/directives/if.md
CHANGED
|
@@ -3,7 +3,7 @@ order: 7
|
|
|
3
3
|
title: model
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# x-model
|
|
7
7
|
|
|
8
8
|
`x-model` allows you to bind the value of an input element to Alpine data.
|
|
9
9
|
|
|
@@ -336,3 +336,39 @@ The default throttle interval is 250 milliseconds, you can easily customize this
|
|
|
336
336
|
```alpine
|
|
337
337
|
<input type="text" x-model.throttle.500ms="search">
|
|
338
338
|
```
|
|
339
|
+
|
|
340
|
+
<a name="programmatic access"></a>
|
|
341
|
+
## Programmatic access
|
|
342
|
+
|
|
343
|
+
Alpine exposes under-the-hood utilities for getting and setting properties bound with `x-model`. This is useful for complex Alpine utilities that may want to override the default x-model behavior, or instances where you want to allow `x-model` on a non-input element.
|
|
344
|
+
|
|
345
|
+
You can access these utilities through a property called `_x_model` on the `x-model`ed element. `_x_model` has two methods to get and set the bound property:
|
|
346
|
+
|
|
347
|
+
* `el._x_model.get()` (returns the value of the bound property)
|
|
348
|
+
* `el._x_model.set()` (sets the value of the bound property)
|
|
349
|
+
|
|
350
|
+
```alpine
|
|
351
|
+
<div x-data="{ username: 'calebporzio' }">
|
|
352
|
+
<div x-ref="div" x-model="username"></div>
|
|
353
|
+
|
|
354
|
+
<button @click="$refs.div._x_model.set('phantomatrix')">
|
|
355
|
+
Change username to: 'phantomatrix'
|
|
356
|
+
</button>
|
|
357
|
+
|
|
358
|
+
<span x-text="$refs.div._x_model.get()"></span>
|
|
359
|
+
</div>
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
<!-- START_VERBATIM -->
|
|
363
|
+
<div class="demo">
|
|
364
|
+
<div x-data="{ username: 'calebporzio' }">
|
|
365
|
+
<div x-ref="div" x-model="username"></div>
|
|
366
|
+
|
|
367
|
+
<button @click="$refs.div._x_model.set('phantomatrix')">
|
|
368
|
+
Change username to: 'phantomatrix'
|
|
369
|
+
</button>
|
|
370
|
+
|
|
371
|
+
<span x-text="$refs.div._x_model.get()"></span>
|
|
372
|
+
</div>
|
|
373
|
+
</div>
|
|
374
|
+
<!-- END_VERBATIM -->
|
package/src/en/directives/on.md
CHANGED
|
@@ -3,7 +3,7 @@ order: 5
|
|
|
3
3
|
title: on
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# x-on
|
|
7
7
|
|
|
8
8
|
`x-on` allows you to easily run code on dispatched DOM events.
|
|
9
9
|
|
|
@@ -13,6 +13,8 @@ Here's an example of simple button that shows an alert when clicked.
|
|
|
13
13
|
<button x-on:click="alert('Hello World!')">Say Hi</button>
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
+
> `x-on` can only listen for events with lower case names, as HTML attribtes are case-insensitive. Writing `x-on:CLICK` will listen for an event named `click`. If you need to listen for a custom event with a camelCase name, you can use the [`.camel` helper](#camel) to work around this limitation. Alternatively, you can use [`x-bind`](/directives/bind.md#bind-directives) to attach an `x-on` directive to an element in javascript code (where case will be preserved).
|
|
17
|
+
|
|
16
18
|
<a name="shorthand-syntax"></a>
|
|
17
19
|
## Shorthand syntax
|
|
18
20
|
|
|
@@ -262,6 +264,19 @@ Sometimes you may want to listen for camelCased events such as `customEvent` in
|
|
|
262
264
|
|
|
263
265
|
By adding `.camel` in the above example, Alpine is now listening for `customEvent` instead of `custom-event`.
|
|
264
266
|
|
|
267
|
+
<a name="dot"></a>
|
|
268
|
+
### .dot
|
|
269
|
+
|
|
270
|
+
```alpine
|
|
271
|
+
<div @custom-event.dot="handleCustomEvent">
|
|
272
|
+
...
|
|
273
|
+
</div>
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
Similar to the `.camelCase` modifier there may be situations where you want to listen for events that have dots in their name (like `custom.event`). Since dots within the event name are reserved by Alpine you need to write them with dashes and add the `.dot` modifier.
|
|
277
|
+
|
|
278
|
+
In the code example above `custom-event.dot` will correspond to the event name `custom.event`.
|
|
279
|
+
|
|
265
280
|
<a name="passive"></a>
|
|
266
281
|
### .passive
|
|
267
282
|
|
package/src/en/directives/ref.md
CHANGED
|
@@ -3,14 +3,14 @@ order: 10
|
|
|
3
3
|
title: transition
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# x-transition
|
|
7
7
|
|
|
8
8
|
Alpine provides a robust transitions utility out of the box. With a few `x-transition` directives, you can create smooth transitions between when an element is shown or hidden.
|
|
9
9
|
|
|
10
10
|
There are two primary ways to handle transitions in Alpine:
|
|
11
11
|
|
|
12
|
-
* [The Transition Helper]()
|
|
13
|
-
* [Applying CSS Classes]()
|
|
12
|
+
* [The Transition Helper](#the-transition-helper)
|
|
13
|
+
* [Applying CSS Classes](#applying-css-classes)
|
|
14
14
|
|
|
15
15
|
<a name="the-transition-helper"></a>
|
|
16
16
|
## The transition helper
|
|
@@ -33,7 +33,7 @@ This is by far the simplest way to get started with Alpine. Include the followin
|
|
|
33
33
|
Notice the `@3.x.x` in the provided CDN link. This will pull the latest version of Alpine version 3. For stability in production, it's recommended that you hardcode the latest version in the CDN link.
|
|
34
34
|
|
|
35
35
|
```alpine
|
|
36
|
-
<script defer src="https://unpkg.com/alpinejs@3.
|
|
36
|
+
<script defer src="https://unpkg.com/alpinejs@3.5.2/dist/cdn.min.js"></script>
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
That's it! Alpine is now available for use inside your page.
|
|
@@ -70,7 +70,7 @@ The two main behavioral differences with this approach are:
|
|
|
70
70
|
## Alpine initialization
|
|
71
71
|
|
|
72
72
|
<a name="alpine-initializing"></a>
|
|
73
|
-
### `
|
|
73
|
+
### `alpine:init`
|
|
74
74
|
|
|
75
75
|
Ensuring a bit of code executes after Alpine is loaded, but BEFORE it initializes itself on the page is a necessary task.
|
|
76
76
|
|
|
@@ -85,7 +85,7 @@ document.addEventListener('alpine:init', () => {
|
|
|
85
85
|
```
|
|
86
86
|
|
|
87
87
|
<a name="alpine-initialized"></a>
|
|
88
|
-
### `
|
|
88
|
+
### `alpine:initialized`
|
|
89
89
|
|
|
90
90
|
Alpine also offers a hook that you can use to execute code After it's done initializing called `alpine:initialized`:
|
|
91
91
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
order: 8
|
|
3
|
+
prefix: $
|
|
4
|
+
title: data
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# $data
|
|
8
|
+
|
|
9
|
+
`$data` is a magic property that gives you access to the current Alpine data scope (generally provided by `x-data`).
|
|
10
|
+
|
|
11
|
+
Most of the time, you can just access Alpine data within expressions directly. for example `x-data="{ message: 'Hello Caleb!' }"` will allow you to do things like `x-text="message"`.
|
|
12
|
+
|
|
13
|
+
However, sometimes it is helpful to have an actual object that encapsulates all scope that you can pass around to other functions:
|
|
14
|
+
|
|
15
|
+
```alpine
|
|
16
|
+
<div x-data="{ greeting: 'Hello' }">
|
|
17
|
+
<div x-data="{ name: 'Caleb' }">
|
|
18
|
+
<button @click="sayHello($data)">Say Hello</button>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
<script>
|
|
23
|
+
function sayHello({ greeting, name }) {
|
|
24
|
+
alert(greeting + ' ' + name + '!')
|
|
25
|
+
}
|
|
26
|
+
</script>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
<!-- START_VERBATIM -->
|
|
30
|
+
<div x-data="{ greeting: 'Hello' }" class="demo">
|
|
31
|
+
<div x-data="{ name: 'Caleb' }">
|
|
32
|
+
<button @click="sayHello($data)">Say Hello</button>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<script>
|
|
37
|
+
function sayHello({ greeting, name }) {
|
|
38
|
+
alert(greeting + ' ' + name + '!')
|
|
39
|
+
}
|
|
40
|
+
</script>
|
|
41
|
+
<!-- END_VERBATIM -->
|
|
42
|
+
|
|
43
|
+
Now when the button is pressed, the browser will alert `Hello Caleb!` because it was passed a data object that contained all the Alpine scope of the expression that called it (`@click="..."`).
|
|
44
|
+
|
|
45
|
+
Most applications won't need this magic property, but it can be very helpful for deeper, more complicated Alpine utilities.
|
|
@@ -3,7 +3,7 @@ order: 5
|
|
|
3
3
|
title: dispatch
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# $dispatch
|
|
7
7
|
|
|
8
8
|
`$dispatch` is a helpful shortcut for dispatching browser events.
|
|
9
9
|
|
|
@@ -56,17 +56,17 @@ Notice that, because of [event bubbling](https://en.wikipedia.org/wiki/Event_bub
|
|
|
56
56
|
<!-- 🚫 Won't work -->
|
|
57
57
|
<div x-data>
|
|
58
58
|
<span @notify="..."></span>
|
|
59
|
-
<button @click="$dispatch('notify')">
|
|
60
|
-
|
|
59
|
+
<button @click="$dispatch('notify')">Notify</button>
|
|
60
|
+
</div>
|
|
61
61
|
|
|
62
62
|
<!-- ✅ Will work (because of .window) -->
|
|
63
63
|
<div x-data>
|
|
64
64
|
<span @notify.window="..."></span>
|
|
65
|
-
<button @click="$dispatch('notify')">
|
|
66
|
-
|
|
65
|
+
<button @click="$dispatch('notify')">Notify</button>
|
|
66
|
+
</div>
|
|
67
67
|
```
|
|
68
68
|
|
|
69
|
-
> The first example won't work because when `custom-event` is dispatched, it'll propagate to its common ancestor, the `div`, not
|
|
69
|
+
> The first example won't work because when `custom-event` is dispatched, it'll propagate to its common ancestor, the `div`, not its sibling, the `<span>`. The second example will work because the sibling is listening for `notify` at the `window` level, which the custom event will eventually bubble up to.
|
|
70
70
|
|
|
71
71
|
<a name="dispatching-to-components"></a>
|
|
72
72
|
## Dispatching to other components
|
|
@@ -84,7 +84,7 @@ You can also take advantage of the previous technique to make your components ta
|
|
|
84
84
|
</div>
|
|
85
85
|
|
|
86
86
|
<div x-data>
|
|
87
|
-
<button @click="$dispatch('set-title', 'Hello World!')"
|
|
87
|
+
<button @click="$dispatch('set-title', 'Hello World!')">Click me</button>
|
|
88
88
|
</div>
|
|
89
89
|
<!-- When clicked, the content of the h1 will set to "Hello World!". -->
|
|
90
90
|
```
|
|
@@ -97,10 +97,10 @@ You can also use `$dispatch()` to trigger data updates for `x-model` data bindin
|
|
|
97
97
|
```alpine
|
|
98
98
|
<div x-data="{ title: 'Hello' }">
|
|
99
99
|
<span x-model="title">
|
|
100
|
-
<button @click="$dispatch('input', 'Hello World!')">
|
|
100
|
+
<button @click="$dispatch('input', 'Hello World!')">Click me</button>
|
|
101
101
|
<!-- After the button is pressed, `x-model` will catch the bubbling "input" event, and update title. -->
|
|
102
102
|
</span>
|
|
103
103
|
</div>
|
|
104
104
|
```
|
|
105
105
|
|
|
106
|
-
This opens up the door for making custom input components
|
|
106
|
+
This opens up the door for making custom input components whose value can be set via `x-model`.
|
package/src/en/magics/el.md
CHANGED
|
@@ -4,7 +4,7 @@ prefix: $
|
|
|
4
4
|
title: nextTick
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
#
|
|
7
|
+
# $nextTick
|
|
8
8
|
|
|
9
9
|
`$nextTick` is a magic property that allows you to only execute a given expression AFTER Alpine has made its 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.
|
|
10
10
|
|
package/src/en/magics/refs.md
CHANGED
|
@@ -4,7 +4,7 @@ prefix: $
|
|
|
4
4
|
title: refs
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
#
|
|
7
|
+
# $refs
|
|
8
8
|
|
|
9
9
|
`$refs` is a magic property that can be used to retrieve DOM elements marked with `x-ref` inside the component. This is useful when you need to manually manipulate DOM elements. It's often used as a more succinct, scoped, alternative to `document.querySelector`.
|
|
10
10
|
|
package/src/en/magics/root.md
CHANGED
package/src/en/magics/store.md
CHANGED
package/src/en/magics/watch.md
CHANGED
|
@@ -104,3 +104,25 @@ Sometimes it's useful to evaluate an expression only the first time an element e
|
|
|
104
104
|
```alpine
|
|
105
105
|
<div x-intersect.once="shown = true">...</div>
|
|
106
106
|
```
|
|
107
|
+
|
|
108
|
+
<a name="half"></a>
|
|
109
|
+
### .half
|
|
110
|
+
|
|
111
|
+
Evaluates the expression once the intersection threshold exceeds `0.5`.
|
|
112
|
+
|
|
113
|
+
Useful for elements where it's important to show at least part of the element.
|
|
114
|
+
|
|
115
|
+
```alpine
|
|
116
|
+
<div x-intersect.half="shown = true">...</div> // when `0.5` of the element is in the viewport
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
<a name="full"></a>
|
|
120
|
+
### .full
|
|
121
|
+
|
|
122
|
+
Evaluates the expression once the intersection threshold exceeds `0.99`.
|
|
123
|
+
|
|
124
|
+
Useful for elements where it's important to show the whole element.
|
|
125
|
+
|
|
126
|
+
```alpine
|
|
127
|
+
<div x-intersect.full="shown = true">...</div> // when `0.99` of the element is in the viewport
|
|
128
|
+
```
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
---
|
|
2
|
+
order: 5
|
|
3
|
+
title: Morph
|
|
4
|
+
description: Morph an element into the provided HTML
|
|
5
|
+
graph_image: https://alpinejs.dev/social_morph.jpg
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Morph Plugin
|
|
9
|
+
|
|
10
|
+
Alpine's Morph plugin allows you to "morph" an element on the page into the provided HTML template, all while preserving any browser or Alpine state within the "morphed" element.
|
|
11
|
+
|
|
12
|
+
This is useful for updating HTML from a server request without loosing Alpine's on-page state. A utility like this is at the core of full-stack frameworks like [Laravel Livewire](https://laravel-livewire.com/) and [Phoenix LiveView](https://dockyard.com/blog/2018/12/12/phoenix-liveview-interactive-real-time-apps-no-need-to-write-javascript).
|
|
13
|
+
|
|
14
|
+
The best way to understand its purpose is with the following interactive visualization. Give it a try!
|
|
15
|
+
|
|
16
|
+
<!-- START_VERBATIM -->
|
|
17
|
+
<div x-data="{ slide: 1 }" class="border rounded">
|
|
18
|
+
<div>
|
|
19
|
+
<img :src="'/img/morphs/morph'+slide+'.png'">
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
<div class="flex w-full justify-between" style="padding-bottom: 1rem">
|
|
23
|
+
<div class="w-1/2 px-4">
|
|
24
|
+
<button @click="slide = (slide === 1) ? 13 : slide - 1" class="w-full bg-brand rounded-full text-center py-3 font-bold text-white">Previous</button>
|
|
25
|
+
</div>
|
|
26
|
+
<div class="w-1/2 px-4">
|
|
27
|
+
<button @click="slide = (slide % 13) + 1" class="w-full bg-brand rounded-full text-center py-3 font-bold text-white">Next</button>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
<!-- END_VERBATIM -->
|
|
32
|
+
|
|
33
|
+
<a name="installation"></a>
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
You can use this plugin by either including it from a `<script>` tag or installing it via NPM:
|
|
37
|
+
|
|
38
|
+
### Via CDN
|
|
39
|
+
|
|
40
|
+
You can include the CDN build of this plugin as a `<script>` tag, just make sure to include it BEFORE Alpine's core JS file.
|
|
41
|
+
|
|
42
|
+
```alpine
|
|
43
|
+
<!-- Alpine Plugins -->
|
|
44
|
+
<script defer src="https://unpkg.com/@alpinejs/morph@3.x.x/dist/cdn.min.js"></script>
|
|
45
|
+
|
|
46
|
+
<!-- Alpine Core -->
|
|
47
|
+
<script defer src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Via NPM
|
|
51
|
+
|
|
52
|
+
You can install Morph from NPM for use inside your bundle like so:
|
|
53
|
+
|
|
54
|
+
```shell
|
|
55
|
+
npm install @alpinejs/morph
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Then initialize it from your bundle:
|
|
59
|
+
|
|
60
|
+
```js
|
|
61
|
+
import Alpine from 'alpinejs'
|
|
62
|
+
import morph from '@alpinejs/morph'
|
|
63
|
+
|
|
64
|
+
Alpine.plugin(morph)
|
|
65
|
+
|
|
66
|
+
...
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
<a name="alpine-morph"></a>
|
|
70
|
+
## Alpine.morph()
|
|
71
|
+
|
|
72
|
+
The `Alpine.morph(el, newHtml)` allows you to imperatively morph a dom node based on passed in HTML. It accepts the following parameters:
|
|
73
|
+
|
|
74
|
+
| Parameter | Description |
|
|
75
|
+
| --- | --- |
|
|
76
|
+
| `el` | A DOM element on the page. |
|
|
77
|
+
| `newHtml` | A string of HTML to use as the template to morph the dom element into. |
|
|
78
|
+
| `options` (optional) | An options object used mainly for [injecting lifecycle hooks](#lifecycle-hooks). |
|
|
79
|
+
|
|
80
|
+
Here's an example of using `Alpine.morph()` to update an Alpine component with new HTML: (In real apps, this new HTML would likely be coming from the server)
|
|
81
|
+
|
|
82
|
+
```alpine
|
|
83
|
+
<div x-data="{ message: 'Change me, then press the button!' }">
|
|
84
|
+
<input type="text" x-model="message">
|
|
85
|
+
<span x-text="message"></span>
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
<button>Run Morph</button>
|
|
89
|
+
|
|
90
|
+
<script>
|
|
91
|
+
document.querySelector('button').addEventListener('click', () => {
|
|
92
|
+
let el = document.querySelector('div')
|
|
93
|
+
|
|
94
|
+
Alpine.morph(el, `
|
|
95
|
+
<div x-data="{ message: 'Change me, then press the button!' }">
|
|
96
|
+
<h2>See how new elements have been added</h2>
|
|
97
|
+
|
|
98
|
+
<input type="text" x-model="message">
|
|
99
|
+
<span x-text="message"></span>
|
|
100
|
+
|
|
101
|
+
<h2>but the state of this component hasn't changed? Magical.</h2>
|
|
102
|
+
</div>
|
|
103
|
+
`)
|
|
104
|
+
})
|
|
105
|
+
</script>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
<!-- START_VERBATIM -->
|
|
109
|
+
<div class="demo">
|
|
110
|
+
<div x-data="{ message: 'Change me, then press the button!' }" id="morph-demo-1" class="space-y-2">
|
|
111
|
+
<input type="text" x-model="message" class="w-full">
|
|
112
|
+
<span x-text="message"></span>
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
<button id="morph-button-1" class="mt-4">Run Morph</button>
|
|
116
|
+
</div>
|
|
117
|
+
|
|
118
|
+
<script>
|
|
119
|
+
document.querySelector('#morph-button-1').addEventListener('click', () => {
|
|
120
|
+
let el = document.querySelector('#morph-demo-1')
|
|
121
|
+
|
|
122
|
+
Alpine.morph(el, `
|
|
123
|
+
<div x-data="{ message: 'Change me, then press the button!' }" id="morph-demo-1" class="space-y-2">
|
|
124
|
+
<h4>See how new elements have been added</h4>
|
|
125
|
+
<input type="text" x-model="message" class="w-full">
|
|
126
|
+
<span x-text="message"></span>
|
|
127
|
+
<h4>but the state of this component hasn't changed? Magical.</h4>
|
|
128
|
+
</div>
|
|
129
|
+
`)
|
|
130
|
+
})
|
|
131
|
+
</script>
|
|
132
|
+
<!-- END_VERBATIM -->
|
|
133
|
+
|
|
134
|
+
<a name="lifecycle-hooks"></a>
|
|
135
|
+
### Lifecycle Hooks
|
|
136
|
+
|
|
137
|
+
The "Morph" plugin works by comparing two DOM trees, the live element, and the passed in HTML.
|
|
138
|
+
|
|
139
|
+
Morph walks both trees simultaneusly and compares each node and its children. If it finds differences, it "patches" (changes) the current DOM tree to match the passed in HTML's tree.
|
|
140
|
+
|
|
141
|
+
While the default algorithm is very capable, there are cases where you may want to hook into its lifecycle and observe or change its behavior as it's happening.
|
|
142
|
+
|
|
143
|
+
Before we jump into the available Lifecycle hooks themselves, let's first list out all the potential parameters they receive and explain what each one is:
|
|
144
|
+
|
|
145
|
+
| Parameter | Description |
|
|
146
|
+
| --- | --- |
|
|
147
|
+
| `el` | This is always the actual, current, DOM element on the page that will be "patched" (changed by Morph). |
|
|
148
|
+
| `toEl` | This is a "template element". It's a temporary element representing what the live `el` will be patched to. It will never actually live on the page and should only be used for reference purposes. |
|
|
149
|
+
| `childrenOnly()` | This is a function that can be called inside the hook to tell Morph to skip the current element and only "patch" its children. |
|
|
150
|
+
| `skip()` | A function that when called within the hook will "skip" comparing/patching itself and the children of the current element. |
|
|
151
|
+
|
|
152
|
+
Here are the available lifecycle hooks (passed in as the third parameter to `Alpine.morph(..., options)`):
|
|
153
|
+
|
|
154
|
+
| Option | Description |
|
|
155
|
+
| --- | --- |
|
|
156
|
+
| `updating(el, toEl, childrenOnly, skip)` | Called before patching the `el` with the comparison `toEl`. |
|
|
157
|
+
| `updated(el, toEl)` | Called after Morph has patched `el`. |
|
|
158
|
+
| `removing(el, skip)` | Called before Morph removes an element from the live DOM. |
|
|
159
|
+
| `removed(el)` | Called after Morph has removed an element from the live DOM. |
|
|
160
|
+
| `adding(el, sip)` | Called before adding a new element. |
|
|
161
|
+
| `added(el)` | Called after adding a new element to the live DOM tree. |
|
|
162
|
+
| `key(el)` | A re-usable function to determine how Morph "keys" elements in the tree before comparing/patching. [More on that here](#keys) |
|
|
163
|
+
| `lookahead` | A boolean value telling Morph to enable an extra feature in its algorithm that "looks ahead" to make sure a DOM element that's about to be removed should instead just be "moved" to a later sibling. |
|
|
164
|
+
|
|
165
|
+
Here is code of all these lifecycle hooks for a more concrete reference:
|
|
166
|
+
|
|
167
|
+
```js
|
|
168
|
+
Alpine.morph(el, newHtml, {
|
|
169
|
+
updating(el, toEl, childrenOnly, skip) {
|
|
170
|
+
//
|
|
171
|
+
},
|
|
172
|
+
|
|
173
|
+
updated(el, toEl) {
|
|
174
|
+
//
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
removing(el, skip) {
|
|
178
|
+
//
|
|
179
|
+
},
|
|
180
|
+
|
|
181
|
+
removed(el) {
|
|
182
|
+
//
|
|
183
|
+
},
|
|
184
|
+
|
|
185
|
+
adding(el, skip) {
|
|
186
|
+
//
|
|
187
|
+
},
|
|
188
|
+
|
|
189
|
+
added(el) {
|
|
190
|
+
//
|
|
191
|
+
},
|
|
192
|
+
|
|
193
|
+
key(el) {
|
|
194
|
+
// By default Alpine uses the `key=""` HTML attribute.
|
|
195
|
+
return el.id
|
|
196
|
+
},
|
|
197
|
+
|
|
198
|
+
lookahead: true, // Default: false
|
|
199
|
+
})
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
<a name="keys"></a>
|
|
203
|
+
### Keys
|
|
204
|
+
|
|
205
|
+
Dom-diffing utilities like Morph try their best to accurately "morph" the original DOM into the new HTML. However, there are cases where it's impossible to determine if an element should be just changed, or replaced completely.
|
|
206
|
+
|
|
207
|
+
Because of this limitation, Morph has a "key" system that allows developers to "force" preserving certain elements rather than replacing them.
|
|
208
|
+
|
|
209
|
+
The most common use-case for them is a list of siblings within a loop. Below is an example of why keys are necessary sometimes:
|
|
210
|
+
|
|
211
|
+
```html
|
|
212
|
+
<!-- "Live" Dom on the page: -->
|
|
213
|
+
<ul>
|
|
214
|
+
<li>Mark</li>
|
|
215
|
+
<li>Tom</li>
|
|
216
|
+
<li>Travis</li>
|
|
217
|
+
</ul>
|
|
218
|
+
|
|
219
|
+
<!-- New HTML to "morph to": -->
|
|
220
|
+
<ul>
|
|
221
|
+
<li>Travis</li>
|
|
222
|
+
<li>Mark</li>
|
|
223
|
+
<li>Tom</li>
|
|
224
|
+
</ul>
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Given the above situation, Morph has no way to know that the "Travis" node has been moved in the DOM tree. It just thinks that "Mark" has been changed to "Travis" and "Travis" changed to "Tom".
|
|
228
|
+
|
|
229
|
+
This is not what we actually want, we want Morph to preserve the original elements and instead of changing them, MOVE them within the `<ul>`.
|
|
230
|
+
|
|
231
|
+
By adding keys to each node, we can accomplish this like so:
|
|
232
|
+
|
|
233
|
+
```html
|
|
234
|
+
<!-- "Live" Dom on the page: -->
|
|
235
|
+
<ul>
|
|
236
|
+
<li key="1">Mark</li>
|
|
237
|
+
<li key="2">Tom</li>
|
|
238
|
+
<li key="3">Travis</li>
|
|
239
|
+
</ul>
|
|
240
|
+
|
|
241
|
+
<!-- New HTML to "morph to": -->
|
|
242
|
+
<ul>
|
|
243
|
+
<li key="3">Travis</li>
|
|
244
|
+
<li key="1">Mark</li>
|
|
245
|
+
<li key="2">Tom</li>
|
|
246
|
+
</ul>
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
Now that there are "keys" on the `<li>`s, Morph will match them in both trees and move them accordingly.
|
|
250
|
+
|
|
251
|
+
You can configure what Morph considers a "key" with the `key:` configutation option. [More on that here](#lifecycle-hooks)
|
|
@@ -194,3 +194,14 @@ Alpine.data('dropdown', function () {
|
|
|
194
194
|
}
|
|
195
195
|
})
|
|
196
196
|
```
|
|
197
|
+
|
|
198
|
+
<a name="using-alpine-persist-global"></a>
|
|
199
|
+
## Using the Alpine.$persist global
|
|
200
|
+
|
|
201
|
+
`Alpine.$persist` is exposed globally so it can be used outside of `x-data` contexts. This is useful to persist data from other sources such as `Alpine.store`.
|
|
202
|
+
|
|
203
|
+
```js
|
|
204
|
+
Alpine.store('darkMode', {
|
|
205
|
+
on: Alpine.$persist(true).as('darkMode_on')
|
|
206
|
+
});
|
|
207
|
+
```
|
package/src/en/plugins/trap.md
CHANGED
|
@@ -178,3 +178,76 @@ Here is nesting in action:
|
|
|
178
178
|
</div>
|
|
179
179
|
</div>
|
|
180
180
|
<!-- END_VERBATIM -->
|
|
181
|
+
|
|
182
|
+
<a name="modifiers"></a>
|
|
183
|
+
## Modifiers
|
|
184
|
+
|
|
185
|
+
<a name="inert"></a>
|
|
186
|
+
### .inert
|
|
187
|
+
|
|
188
|
+
When building things like dialogs/modals, it's recommended to hide all the other elements on the page from screenreaders when trapping focus.
|
|
189
|
+
|
|
190
|
+
By adding `.inert` to `x-trap`, when focus is trapped, all other elements on the page will receive `aria-hidden="true"` attributes, and when focus trapping is disabled, those attributes will also be removed.
|
|
191
|
+
|
|
192
|
+
```alpine
|
|
193
|
+
<!-- When `open` is `false`: -->
|
|
194
|
+
<body x-data="{ open: false }">
|
|
195
|
+
<div x-trap.inert="open" ...>
|
|
196
|
+
...
|
|
197
|
+
</div>
|
|
198
|
+
|
|
199
|
+
<div>
|
|
200
|
+
...
|
|
201
|
+
</div>
|
|
202
|
+
</body>
|
|
203
|
+
|
|
204
|
+
<!-- When `open` is `true`: -->
|
|
205
|
+
<body x-data="{ open: true }">
|
|
206
|
+
<div x-trap.inert="open" ...>
|
|
207
|
+
...
|
|
208
|
+
</div>
|
|
209
|
+
|
|
210
|
+
<div aria-hidden="true">
|
|
211
|
+
...
|
|
212
|
+
</div>
|
|
213
|
+
</body>
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
<a name="noscroll"></a>
|
|
217
|
+
### .noscroll
|
|
218
|
+
|
|
219
|
+
When building dialogs/modals with Alpine, it's recommended that you disable scrollling for the surrounding content when the dialog is open.
|
|
220
|
+
|
|
221
|
+
`x-trap` allows you to do this automatically with the `.noscroll` modifiers.
|
|
222
|
+
|
|
223
|
+
By adding `.noscroll`, Alpine will remove the scrollbar from the page and block users from scrolling down the page while a dialog is open.
|
|
224
|
+
|
|
225
|
+
For example:
|
|
226
|
+
|
|
227
|
+
```alpine
|
|
228
|
+
<div x-data="{ open: false }">
|
|
229
|
+
<button>Open Dialog</button>
|
|
230
|
+
|
|
231
|
+
<div x-show="open" x-trap.noscroll="open">
|
|
232
|
+
Dialog Contents
|
|
233
|
+
|
|
234
|
+
<button @click="open = false">Close Dialog</button>
|
|
235
|
+
</div>
|
|
236
|
+
</div>
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
<!-- START_VERBATIM -->
|
|
240
|
+
<div class="demo">
|
|
241
|
+
<div x-data="{ open: false }">
|
|
242
|
+
<button @click="open = true">Open Dialog</button>
|
|
243
|
+
|
|
244
|
+
<div x-show="open" x-trap.noscroll="open" class="border mt-4 p-4">
|
|
245
|
+
<div class="mb-4 text-bold">Dialog Contents</div>
|
|
246
|
+
|
|
247
|
+
<p class="mb-4 text-gray-600 text-sm">Notice how you can no longer scroll on this page while this dialog is open.</p>
|
|
248
|
+
|
|
249
|
+
<button class="mt-4" @click="open = false">Close Dialog</button>
|
|
250
|
+
</div>
|
|
251
|
+
</div>
|
|
252
|
+
</div>
|
|
253
|
+
<!-- END_VERBATIM -->
|
package/src/en/upgrade-guide.md
CHANGED
|
@@ -40,7 +40,7 @@ Upgrading from Alpine V2 to V3 should be fairly painless. In many cases, NOTHING
|
|
|
40
40
|
<a name="el-no-longer-root"></a>
|
|
41
41
|
### `$el` is now always the current element
|
|
42
42
|
|
|
43
|
-
`$el` now always represents the element that an expression was executed on, not the root element of the component. This will replace most usages of `x-ref` and in the cases where you still want to access the root of a component, you can do so using
|
|
43
|
+
`$el` now always represents the element that an expression was executed on, not the root element of the component. This will replace most usages of `x-ref` and in the cases where you still want to access the root of a component, you can do so using `$root`. For example:
|
|
44
44
|
|
|
45
45
|
```alpine
|
|
46
46
|
<!-- 🚫 Before -->
|
|
@@ -50,30 +50,15 @@ Upgrading from Alpine V2 to V3 should be fairly painless. In many cases, NOTHING
|
|
|
50
50
|
</div>
|
|
51
51
|
|
|
52
52
|
<!-- ✅ After -->
|
|
53
|
-
<div x-data
|
|
54
|
-
<button @click="console.log($
|
|
53
|
+
<div x-data>
|
|
54
|
+
<button @click="console.log($root)"></button>
|
|
55
55
|
</div>
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
-
For a smoother upgrade experience, you can
|
|
59
|
-
|
|
60
|
-
```alpine
|
|
61
|
-
<script>
|
|
62
|
-
document.addEventListener('alpine:init', () => {
|
|
63
|
-
Alpine.magic('root', el => {
|
|
64
|
-
let closestRootEl = (node) => {
|
|
65
|
-
if (node.hasAttribute('x-data')) return node
|
|
66
|
-
|
|
67
|
-
return closestRootEl(node.parentNode)
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return closestRootEl(el)
|
|
71
|
-
})
|
|
72
|
-
})
|
|
73
|
-
</script>
|
|
74
|
-
```
|
|
58
|
+
For a smoother upgrade experience, you can replace all instances of `$el` with a custom magic called `$root`.
|
|
75
59
|
|
|
76
|
-
[→ Read more about $el in V3](/magics/el)
|
|
60
|
+
[→ Read more about $el in V3](/magics/el)
|
|
61
|
+
[→ Read more about $root in V3](/magics/root)
|
|
77
62
|
|
|
78
63
|
<a name="auto-init"></a>
|
|
79
64
|
### Automatically evaluate `init()` functions defined on data object
|