mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[ADD] misc: component Portal
The component Portal is used to teleport the content in its slot as a child of an element somewhere else in the DOM, usually 'body' It is designed to be transparent and is just here to do the teleportation task OwlEvents (with method `Component.trigger`) are redirected from any Component instanciated within the Portal (and therefore teleported somewhere else) to the place it would have been without teleportation i.e. they are re-triggered onto the Portal root node Co-authored-by: Aaron Bohy <aab@odoo.com> closes #184 closes #466
This commit is contained in:
committed by
Géry Debongnie
parent
821bd0b4b8
commit
556a4644f0
@@ -1,5 +1,116 @@
|
||||
# 🦉 Miscellaneous 🦉
|
||||
|
||||
## Content
|
||||
|
||||
- [Portal](#portal)
|
||||
- [AsyncRoot](#asyncroot)
|
||||
|
||||
## `Portal`
|
||||
|
||||
### Overview
|
||||
|
||||
The component `Portal` is meant to be used as a transparent way to 'teleport' a piece
|
||||
of DOM to the node represented by its sole `target` props.
|
||||
|
||||
This component aims at helping the implementation of the needed infrastructure
|
||||
for modals (as in `bootstrap-modal`).
|
||||
|
||||
### Usage
|
||||
|
||||
The content it will teleport is defined within the `<Portal>` node and
|
||||
internally uses the `default` [Slot](component.md#slots).
|
||||
|
||||
This slot must contain only **one** node, which in turn can have as many children as necessary.
|
||||
|
||||
The element under which the content will be teleported is represented as a selector
|
||||
by the `target` props which only accepts a string as value.
|
||||
|
||||
The `target` props only supports static selector, and is not meant to be passed to `Portal`
|
||||
as a variable. Namely, `<Portal target="'body'" />` is the intended use.
|
||||
By contrast, `<Portal target="state.target" />` is not supported.
|
||||
|
||||
The component `Portal` has no particular state, rather it is meant to be a slave to its parent,
|
||||
and ultimately just a way for the parent to teleport a piece of its own DOM elsewhere.
|
||||
|
||||
The `Portal`'s root node is always `<portal/>` and is placed where the teleported content
|
||||
*would have* been. It is this element that the [teleported events](#expected-behaviors) are re-directed on.
|
||||
|
||||
### Example
|
||||
|
||||
The canonic use-case is to implement a Dialog, where a Component may choose to break the natural
|
||||
workflow to help the user put in some data, which it could use later on.
|
||||
|
||||
JavaScript:
|
||||
|
||||
```js
|
||||
const { Component } = owl;
|
||||
const { Portal } = owl.misc;
|
||||
|
||||
class TeleportedComponent extends Component {}
|
||||
class App extends Component {
|
||||
static components = { Portal, TeleportedComponent };
|
||||
}
|
||||
|
||||
const app = new App();
|
||||
app.mount(document.body);
|
||||
```
|
||||
|
||||
XML:
|
||||
|
||||
```xml
|
||||
<templates>
|
||||
<div t-name="TeleportedComponent">
|
||||
<span>I will move soon enough</span>
|
||||
</div>
|
||||
|
||||
<div t-name="App">
|
||||
<span>I am like the rest of us</span>
|
||||
<Portal target="'body'">
|
||||
<TeleportedComponent />
|
||||
</Portal>
|
||||
</div>
|
||||
</templates>
|
||||
```
|
||||
|
||||
In this example, the `Portal` component will teleport the `TeleportedComponent`'s `div` as a child of the `body`.
|
||||
`TeleportedComponent` is acting as a Dialog here.
|
||||
|
||||
The resulting DOM will look like:
|
||||
|
||||
```xml
|
||||
<body>
|
||||
<div>
|
||||
<span>I am like the rest of us</span>
|
||||
<portal></portal>
|
||||
</div>
|
||||
<div>
|
||||
<span>I will move soon enough</span>
|
||||
</div>
|
||||
</body>
|
||||
```
|
||||
|
||||
### Expected Behaviors
|
||||
|
||||
The teleported piece is updated as any other `Component`'s DOM and in the same sequence.
|
||||
Namely the teleported piece will be updated in function of its parents components, and patched as
|
||||
a normal child.
|
||||
|
||||
The [_business_ events](component.md#event-handling) triggered by a child component will be stopped
|
||||
to not bubble outside of the `target`. They will, on the other hand, be re-directed onto the
|
||||
`Portal`'s root node and bubble up the DOM as if it were triggered by a regular child component.
|
||||
|
||||
Beware that those re-directed events are copies of the original event.
|
||||
They have:
|
||||
|
||||
- The same payload.
|
||||
- The same `originalComponent` than their original counterpart,
|
||||
that is the actual Component that triggered it.
|
||||
- A **different** `target` property than their original counterpart.
|
||||
The `target` of a re-directed event is necessarily the `Portal`'s root node.
|
||||
|
||||
Pure DOM events do not follow this pattern and are free to bubble their natural, unaltered way
|
||||
up to the `body`.
|
||||
|
||||
## `AsyncRoot`
|
||||
|
||||
When this component is used, a new rendering sub tree is created, such that the
|
||||
|
||||
Reference in New Issue
Block a user