Oftentimes I find myself writing components that semantically belong where they are but need to be displayed somewhere else in my app. Teleport is there to solve the problem - but you can only port outside of the app. Whenever I target an element which is rendered by vue itself it echos a warning that the element needs to be mounted first. Unfortunately, not every usecase can be rewritten in a way, that it falls into the simply modal-button category.
I opened a stackoverflow issue with a reproduction: https://stackoverflow.com/questions/63652288/does-vue-3-teleport-only-works-to-port-outside-vue
Either have a teleport target component as in portal-vue or allow targeting ids of other components
I think the following approach should work correctly:
createApp({
setup() {
const refDom = ref(null)
return () => [
// ref the DOM
h('div', { ref: refDom }),
h(Teleport, {
// set the target to refDom
to: refDom.value,
// disable teleport when refDom is not set
disabled: !refDom.value
}, h('h1', 'hcy'))
]
}
}).mount('#app')
But that will still get warning messages:
[Vue warn]: Invalid Teleport target: null
[Vue warn]: Invalid Teleport target on mount: null (object)
Maybe we need to improve it.
Interesting approach. But it becomes more complicated when you dont have access to the dom ref because the target is in some different component. How would you go about that?
works well
const useTele = () => {
const target = ref(null)
return () => target
}
const useThisTele = useTele()
createApp({
setup() {
return { target: useThisTele() }
},
template: `
<div>
<h1>App</h1>
<div id="dest" :ref="d => target = d"></div>
<parent-comp/>
</div>`
}).component('parent-comp', {
template: `
<div>
<child-comp/>
</div>`
}).component('child-comp', {
setup() {
return { target: useThisTele() }
},
template: `
<div>
<Teleport :to="target" :disabled="!target">
Hello From Portal
</Teleport>
</div>`
}).mount('#app')
That still a bit fragile, however.
When the target's parent gets removed from the DOM, the portal content is removed from the DOM with it - but the source component won't be informed about this and consequently, in it's vdom, it assumes that the elements still are in the DOM. That will likely result in update errors when the source component does update later.
@unbyte so you basically pass (or import) useThisRef to the components which define it and which uses it as target. Is that correct? (so in easy terms you pass around a reference)
@LinusBorg , this discussion might help, where the terminology "reparenting" is used:
Generally the discussion (incl. linked gists+rfc) revolves around the use of keys/instances to map components to their destinations but two libraries also stick out there lately:
Has any similar discussion taken place in vue or its rfcs or is there even a vue lib that solves this problem already?
Most helpful comment
That still a bit fragile, however.
When the target's parent gets removed from the DOM, the portal content is removed from the DOM with it - but the source component won't be informed about this and consequently, in it's vdom, it assumes that the elements still are in the DOM. That will likely result in update errors when the source component does update later.