@alpinejs/docs 3.6.0-revision.1 → 3.6.1-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
CHANGED
|
@@ -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.6.
|
|
36
|
+
<script defer src="https://unpkg.com/alpinejs@3.6.1/dist/cdn.min.js"></script>
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
That's it! Alpine is now available for use inside your page.
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
---
|
|
2
|
+
order: 6
|
|
3
|
+
title: Portal
|
|
4
|
+
description: Send Alpine templates to other parts of the DOM
|
|
5
|
+
graph_image: https://alpinejs.dev/social_portal.jpg
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Portal Plugin
|
|
9
|
+
|
|
10
|
+
Alpine's Portal plugin allows you to transport part of your Alpine template to another part of the DOM on the page entirely.
|
|
11
|
+
|
|
12
|
+
This is useful for things like modals (especially nesting them), where it's helpful to break out of the z-index of the current Alpine component.
|
|
13
|
+
|
|
14
|
+
> Note: this plugin is currently in beta while it is being tested in the public. Be warned that it may change before being officially released.
|
|
15
|
+
|
|
16
|
+
<a name="installation"></a>
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
You can use this plugin by either including it from a `<script>` tag or installing it via NPM:
|
|
20
|
+
|
|
21
|
+
### Via CDN
|
|
22
|
+
|
|
23
|
+
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.
|
|
24
|
+
|
|
25
|
+
```alpine
|
|
26
|
+
<!-- Alpine Plugins -->
|
|
27
|
+
<script defer src="https://unpkg.com/@alpinejs/portal@3.6.0-beta.0/dist/cdn.min.js"></script>
|
|
28
|
+
|
|
29
|
+
<!-- Alpine Core -->
|
|
30
|
+
<script defer src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Via NPM
|
|
34
|
+
|
|
35
|
+
You can install Portal from NPM for use inside your bundle like so:
|
|
36
|
+
|
|
37
|
+
```shell
|
|
38
|
+
npm install @alpinejs/portal
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Then initialize it from your bundle:
|
|
42
|
+
|
|
43
|
+
```js
|
|
44
|
+
import Alpine from 'alpinejs'
|
|
45
|
+
import portal from '@alpinejs/portal'
|
|
46
|
+
|
|
47
|
+
Alpine.plugin(portal)
|
|
48
|
+
|
|
49
|
+
...
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
<a name="usage"></a>
|
|
53
|
+
## Usage
|
|
54
|
+
|
|
55
|
+
Everytime you use a portal, you will need two different directives: `x-portal` and `x-portal-target`.
|
|
56
|
+
|
|
57
|
+
By attaching `x-portal` to a `<template>` element, you are telling Alpine to send that DOM content to another template element that has a matching `x-portal-target` on it.
|
|
58
|
+
|
|
59
|
+
Here's a contrived modal example using portals:
|
|
60
|
+
|
|
61
|
+
```alpine
|
|
62
|
+
<div x-data="{ open: false }">
|
|
63
|
+
<button @click="open = ! open">Toggle Modal</button>
|
|
64
|
+
|
|
65
|
+
<template x-portal="modals">
|
|
66
|
+
<div x-show="open">
|
|
67
|
+
Modal contents...
|
|
68
|
+
</div>
|
|
69
|
+
</template>
|
|
70
|
+
|
|
71
|
+
<div class="py-4">Some other content placed AFTER the modal markup.</div>
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
<template x-portal-target="modals"></template>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
<!-- START_VERBATIM -->
|
|
78
|
+
<div class="demo" x-ref="root">
|
|
79
|
+
<div x-data="{ open: false }">
|
|
80
|
+
<button @click="open = ! open">Toggle Modal</button>
|
|
81
|
+
|
|
82
|
+
<template x-portal="modals">
|
|
83
|
+
<div x-show="open">
|
|
84
|
+
Modal contents...
|
|
85
|
+
</div>
|
|
86
|
+
</template>
|
|
87
|
+
|
|
88
|
+
<div class="py-4">Some other content...</div>
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
<template x-portal-target="modals"></template>
|
|
92
|
+
</div>
|
|
93
|
+
<!-- END_VERBATIM -->
|
|
94
|
+
|
|
95
|
+
Notice how when toggling the modal, the actual modal contents show up AFTER the "Some other content..." element? This is because when Alpine is initializing, it sees `x-portal="modals"` and takes that markup out of the page waiting until it finds an element with `x-portal-target="modals"` to insert it into.
|
|
96
|
+
|
|
97
|
+
<a name="forwarding-events"></a>
|
|
98
|
+
## Forwarding events
|
|
99
|
+
|
|
100
|
+
Alpine tries it's best to make the experience of using portals seemless. Anything you would normally do in a template, you should be able to do inside a portal. Portal content can access the normal Alpine scope of the component as well as other features like `$refs`, `$root`, etc...
|
|
101
|
+
|
|
102
|
+
However, native DOM events have no concept of portals, so if, for example, you trigger a "click" event from inside a portal, that event will bubble up the DOM tree as it normally would ignoring the fact that it is within a portal.
|
|
103
|
+
|
|
104
|
+
To make this experience more seemless, you can "forward" events by simply registering event listeners on the portal's `<template>` element itself like so:
|
|
105
|
+
|
|
106
|
+
```alpine
|
|
107
|
+
<div x-data="{ open: false }">
|
|
108
|
+
<button @click="open = ! open">Toggle Modal</button>
|
|
109
|
+
|
|
110
|
+
<template x-portal="modals" @click="open = false">
|
|
111
|
+
<div x-show="open">
|
|
112
|
+
Modal contents...
|
|
113
|
+
(click to close)
|
|
114
|
+
</div>
|
|
115
|
+
</template>
|
|
116
|
+
</div>
|
|
117
|
+
|
|
118
|
+
<template x-portal-target="modals"></template>
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
<!-- START_VERBATIM -->
|
|
122
|
+
<div class="demo" x-ref="root">
|
|
123
|
+
<div x-data="{ open: false }">
|
|
124
|
+
<button @click="open = ! open">Toggle Modal</button>
|
|
125
|
+
|
|
126
|
+
<template x-portal="modals" @click="open = false">
|
|
127
|
+
<div x-show="open">
|
|
128
|
+
Modal contents...<br>
|
|
129
|
+
(click to close)
|
|
130
|
+
</div>
|
|
131
|
+
</template>
|
|
132
|
+
</div>
|
|
133
|
+
|
|
134
|
+
<template x-portal-target="modals"></template>
|
|
135
|
+
</div>
|
|
136
|
+
<!-- END_VERBATIM -->
|
|
137
|
+
|
|
138
|
+
Notice how we are now able to listen for events dispatched from within the portal from outside the portal itself?
|
|
139
|
+
|
|
140
|
+
Alpine does this by looking for event listeners registered on `<template x-portal...` and stops those events from propogating past the `<template x-portal-target...` element. Then it creates a copy of that event and re-dispatches it from `<template x-portal`.
|
|
141
|
+
|
|
142
|
+
<a name="nesting-portals"></a>
|
|
143
|
+
## Nesting portals
|
|
144
|
+
|
|
145
|
+
Portals are especially helpful if you are trying to nest one modal within another. Alpine makes it simple to do so:
|
|
146
|
+
|
|
147
|
+
```alpine
|
|
148
|
+
<div x-data="{ open: false }">
|
|
149
|
+
<button @click="open = ! open">Toggle Modal</button>
|
|
150
|
+
|
|
151
|
+
<template x-portal="modals">
|
|
152
|
+
<div x-show="open">
|
|
153
|
+
Modal contents...
|
|
154
|
+
|
|
155
|
+
<div x-data="{ open: false }">
|
|
156
|
+
<button @click="open = ! open">Toggle Nested Modal</button>
|
|
157
|
+
|
|
158
|
+
<template x-portal="modals">
|
|
159
|
+
<div x-show="open">
|
|
160
|
+
Nested modal contents...
|
|
161
|
+
</div>
|
|
162
|
+
</template>
|
|
163
|
+
</div>
|
|
164
|
+
</div>
|
|
165
|
+
</template>
|
|
166
|
+
</div>
|
|
167
|
+
|
|
168
|
+
<template x-portal-target="modals"></template>
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
<!-- START_VERBATIM -->
|
|
172
|
+
<div class="demo" x-ref="root">
|
|
173
|
+
<div x-data="{ open: false }">
|
|
174
|
+
<button @click="open = ! open">Toggle Modal</button>
|
|
175
|
+
|
|
176
|
+
<template x-portal="modals">
|
|
177
|
+
<div x-show="open">
|
|
178
|
+
<div class="py-4">Modal contents...</div>
|
|
179
|
+
|
|
180
|
+
<div x-data="{ open: false }">
|
|
181
|
+
<button @click="open = ! open">Toggle Nested Modal</button>
|
|
182
|
+
|
|
183
|
+
<template x-portal="modals">
|
|
184
|
+
<div class="pt-4" x-show="open">
|
|
185
|
+
Nested modal contents...
|
|
186
|
+
</div>
|
|
187
|
+
</template>
|
|
188
|
+
</div>
|
|
189
|
+
</div>
|
|
190
|
+
</template>
|
|
191
|
+
</div>
|
|
192
|
+
|
|
193
|
+
<template x-portal-target="modals"></template>
|
|
194
|
+
</div>
|
|
195
|
+
<!-- END_VERBATIM -->
|
|
196
|
+
|
|
197
|
+
After toggling "on" both modals, they are authored as children, but will be rendered as sibling elements on the page, not within one another.
|
|
198
|
+
|
|
199
|
+
<a name="multiple-portals"></a>
|
|
200
|
+
## Handling multiple portals
|
|
201
|
+
|
|
202
|
+
Suppose you have multiple modals on a page, but a single `<template x-portal-target="modal">` element.
|
|
203
|
+
|
|
204
|
+
Alpine automatically appends extra elements with `x-portal="modals"` at the target. No need for any extra syntax:
|
|
205
|
+
|
|
206
|
+
```alpine
|
|
207
|
+
<template x-portal="modals">
|
|
208
|
+
...
|
|
209
|
+
</template>
|
|
210
|
+
|
|
211
|
+
<template x-portal="modals">
|
|
212
|
+
...
|
|
213
|
+
</template>
|
|
214
|
+
|
|
215
|
+
...
|
|
216
|
+
|
|
217
|
+
<template x-portal-target="modals"></template>
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Now both of these modals will be rendered where `<template x-portal-target="modals">` lives.
|