I'm trying to create a BaseOverlay
component that basically teleports its content to a certain area of my application. It works just fine except there's an issue when using it with v-show
... I think because my component's root is a Teleport
that v-show won't work because Teleport is a template.
I figured I could then use inheritAttrs: false
and v-bind="$attrs"
on the inner content... this throws a warning from Vue saying Runtime directive used on component with non-element root node. The directives will not function as intended.
It results in v-show
not working on MyComponent
, but v-if
does work.
Any clues as to what I'm doing wrong?
App.vue
<script setup>
import MyComponent from "./MyComponent.vue";
import {ref} from "vue";
const showOverlay = ref(false);
function onClickButton() {
showOverlay.value = !showOverlay.value;
}
</script>
<template>
<button @click="onClickButton">
Toggle Showing
</button>
<div id="overlays" />
<div>
Hello World
</div>
<MyComponent v-show="showOverlay" text="Doesn't work" />
<MyComponent v-if="showOverlay" text="Works" />
</template>
BaseOverlay.vue
<template>
<Teleport to="#overlays">
<div
class="overlay-container"
v-bind="$attrs"
>
<slot />
</div>
</Teleport>
</template>
<script>
export default {
name: "BaseOverlay",
inheritAttrs: false,
};
</script>
MyComponent.vue
<template>
<BaseOverlay>
{{text}}
</BaseOverlay>
</template>
<script>
import BaseOverlay from "./BaseOverlay.vue";
export default {
name: "MyComponent",
components: {
BaseOverlay
},
props: {
text: {
type: String,
default: ""
}
}
}
</script>
Wanted to follow-up on this. I started running into a lot of other issues with
Teleport
, like the inability to use it as a root element in a component (like a Dialog component because I want to teleport all dialogs to a certain area of the application), and some other strange issues withKeepAlive
.I ended up rolling my own WebComponent and using that instead. I have an
OverlayManager
WebComponent that is used within theBaseOverlay
component, and every time aBaseOverlay
is mounted, it adds itself to theOverlayManager
.Example
OverlayManager.js
BaseOverlay.vue
App.vue
This behaves exactly how I need it, and I don't have to deal with the quirks that
Teleport
introduces, and it allows me to have a singleton that is in charge of all of my overlays. The other benefit is that I have access to the parent of whereBaseOverlay
is initially added in the HTML (not where it's moved). Honestly not sure if this is a good practice, but I'm chuffed at how cute this is and how well Vue integrates with it.