Advanced Usage
Switching targets & sources
The to
prop of <portal>
and the name
prop of <portal-target>
can be changed dynamically with v-bind
, allowing you to send content of one <portal>
to a different <portal-target>
, or switch the source of a <portal-target>
from one <portal>
to another.
<portal v-bind:to="name">
Content will be dynamically sent to the destination that `name` evaluates to
</portal>
<portal-target v-bind:name="name">
by changing the 'name', you can define which portal's content should be shown.
</portal-target>
2
3
4
5
6
7
"Slim" - removing the wrapper
Vue components always need a single root element. Since <portal-target>
can't know in advance whether or not it will receive content with more than one root element, it will render a wrapper element around the content to provide a single root node.
However, if you know that you will send content with a single root element only, you can use the slim
prop to tell <portal-target>
to render that element only and do without the wrapper element.
This can be useful if <portal-target>
s wrapper element is creating problems for your styling.
<portal to="destination">
<div>
<p>This content has a single root node (the div)</p>
<p>
Therefore, we can tell the PortalTarget to do without a root element of
its own
</p>
</div>
</portal>
<portal-target name="destination" slim />
2
3
4
5
6
7
8
9
10
11
The slim
property also works on <portal>
components when they are disabled
(see here).
1.3.0+
Scoped SlotsPortalVue can also be used with Scoped Slots! This allows you to send a scoped slot to a PortalTarget, which can then provide props for the slot content:
<portal to="destination">
<p slot-scope="{message}">{{message}}</p>
</portal>
<portal-target
name="destination"
:slot-props="{message: 'Hello from the Target to You!'}"
/>
2
3
4
5
6
7
8
Result:
<div class="vue-portal-target">
<p>Hello from the Target to You!</p>
</div>
2
3
1.2.0+
TransitionsYou can pass transitions to a <portal>
without problems. It will behave just the same when the content is being rendered in the <portal-target>
:
<portal to="destination">
<transition name="fade">
<p v-if="hasMessages" key="1">You have {{messages.length}} new messages</p>
<p v-else key="2">No unread messages</p>
</transition>
</portal>
2
3
4
5
6
However, if you use a <portal-target>
for multiple <portal>
s, you likely want to define the transition on the target end instead. This is also supported.
WARNING
This API underwent a significant change in the 2.0.0 release. Below, examples for both old and new syntax are given. Keep an eye on the version badges next to them.
2.0.0+
New Syntax<portal-target
transition="fade"
/>
2
3
Here, the string 'fade'
would be expected to be the name of a globally registered component that wraps a <transition>
component (see Vue docs on reusable transitions). You can also pass a component options object or a constructor. We have more examples in the API docs.
>=1.2 <2.0
Old Syntax<portal-target
:transition="{ name: 'fade'}"
:transition-events="{ enter: onEnterCallBack }"
/>
2
3
4
One important behaviour to know is this:
- When the PortalTarget would render only one content element, a
<transition>
is created. - When it would render multiple elements, the rendered root wrapper element will be turned into a
<transition-group>
component instead.
2.0.0+
Rendering outside of the Vue-AppIf you want to render your content to a place outside of the control of your Vue app, Portal-Vue also got your covered. It gives you a special <MountingPortal>
component that mounts a <PortalTarget>
to any element in the DOM - you just define it with a normal DOM selector.
It then provides the (auto-generated) name of the generated Target to its children through a scoped slot.
<div id="app">
<MountingPortal mountTo="#widget" name="source" append>
<p>Content for the Target</p>
</MountingPortal>
<div>
<script>
new Vue({el: '#app'})
</script>
<aside id="widget" class="widget-sidebar">
This Element is not controlled by our Vue-App,
but we can create a <portal-target> here with <MountingPortal>.
</aside>
2
3
4
5
6
7
8
9
10
11
12
13
When <MountingPortal>
is destroyed, it takes care of destroying the <PortalTarget>
.
TIP
When the append
prop is set, the Target will be mounted to as a child of the specified element instead of replacing it.
This is great if you want to mount more than one PortalTarget there, for example.
When append
is used, <MountingPortal>
will also remove the appended element when destroying the <PortalTarget>
targetEl
- The Old Way 1.* only
<body>
<div id="app">
<portal name="" target-el="#widget">
<p>
PortalVue will dynamically mount an instance of <portal-target>
in place of the Element with `id="widget"`,
and this paragraph will be rendered inside of it.
</p>
</portal>
</div>
</body>
2
3
4
5
6
7
8
9
10
11
WARNING
This feature had a couple of problems that were the trigger to revamp it for 2.0 as can be seen above.
It was both a bit buggy and made the code harder to maintain, so we extracted it into a separate component for 2.0