Closed lazlo-bonin closed 6 years ago
Hm, that's tricky one. Neve thought of that use case.
The infinite loop is because you are essentially sending content from within the target to itself over and over.
I can't think of a workaround for you on the spot. Maybe this is fixable in the lib if we somehow can make a <portal>
realize it's itself being rendered in the <portal-target>
its sending its content to.
For a secon I thought a solution based on provide/inject
would work, but it won't, since the $parent
chain of the portal won't contain the <portal-target >
. But maybe I can make that $parent redirection optional?
Anyhow, I have to think about this more deeply. As a heads up I will tell you that I'm in the middle of preparing for a move to a new flat, as well as some trips, so I probably won't find time to work on this in the coming weeks until March.
Help would be welcome, even if it's just throwing in ideas.
Thanks for the quick reply; unfortunately this is an urgent issue for our project which is nearing deployment in a few days. If you can think of any workaround, please let me know!
I will think about it a bit, but will be on by brother's bachelor trip this weekend, so there won't be much time.
As I indicated previously, a not so clean but maybe doable workaround would be to provide a sequence of targets (like z-indexes) and tell the dialog compontent to which one to portal with a prop.
Maybe you could even automate that by having your portal check how many other dialogs are in its $parent chain and determine the right target accordingly
Example: Given these targets:
<portal-target name="level-0" />
<portal-target name="level-1" />
<portal-target name="level-2" />
You could dynamically determine how many dialogs you are nested by doing this in your dialog:
function getLevel() {
let level = 0
let parent = this.$parent
while (parent) {
if (parent.$options.name === 'dialog') {
level++
}
parent = parent.$parent
}
return level
}
<portal :to="`level-${getLevel()}`">
Obviously if you have different kinds of controls that can be nested with portals inside them (dropdowns ...) the detection via parent.$options.name
would have take this into account, but I hope this gets you on a workable path until we find a proper solution.
Clever workaround! Tried it, but it seems like parent becomes the portal target's parent itself once the dialog is moved by PortalVue. Therefore, getLevel()
always returns 0 and the problem persists. In other words, I think PortalVue moves the DOM before getLevel()
gets called.
Hm I didn't expect that, won't be able to test this weekend, unfortunately.
I thought it should work because I explicitly took care in the lib that $parent inside a portal points to the portal's parent, not the target's.
Other workaround I tried: having a global dialogLevel
variable that gets incremented when the dialog component is mounted, and decremented when it gets destroyed. However, it seems that the component gets mounted/destroyed when being moved by PortalVue, and thus going back to the infinite loop...
Edit: that was just me being daft about Vue's property watching. This workaround works!
data() {
return {
level: 0
}
},
mounted() {
this.level = window.app.dialogLevel;
window.app.dialogLevel++;
},
destroyed() {
window.app.dialogLevel--;
}
Oh, now I see what's happening... of course. The template with all nested slots is fully turned into vnodes within the parent component's context, and then the vnodes are passed down through the slots. So this won't work indeed.
Still should work (or could, don't want to sound too secure) if:
<wrapped-portal to="level">
</wrapped-portal>
// wrapped-portal.js
export default {
render(h) {
props: ['to'],
return h('portal', {
props: {
...this.$attrs,
to: this.to + '-' + this.getLevel()
}
}, this.$slots.default)
},
methods: {
getLevel() { ... }
}
}
Might work better...
Anyway, I won't be able to do much more until sunday, maybe even tuesday, sorry :/
See my edit, I found a working, automated workaround :) Thanks for all the help!
Oh, glad it works! Good luck with the deadline!
I make this component. It's cut-version, in full I mark the previous modal as "freezed" to remove the background and wrap all in animations with anime.js.
There is some strange moments like `this.mountedComp.$el.parentNode.removeChild(this.mountedComp.$el);`
But only with it it working (include hot module replacement).
Modal.vue
```
I'm not surei I understand what has to do with IOS issue of nested portals, can you explain?
@LinusBorg do you ask me?
Yes
Sorry if my message not clear. I just show my component, that solve problem with nesting portals.
I decided to close this feature request as it hasn't come up from anyone else so far and would be very complex to solve.
Actually, I would have a use case for this one :
It would be possible to handle this via vuex but I would lose all routing/hierarchy info. I had a glimmer of hope when I saw this lib, but this just crushed it.
Could you point me to a potential solution, so I can see if I can help ? Would adding a unique id to source portals potentially solve the problem ?
OK, named portals and a specialized container worked... Thanks for the lib :)
I was about to create a new issue for 3.0 but realised this existing one which is on the same subject so decided to post it here. (sry for reviving this super old thread š )
@LinusBorg What's weirder to me is that nested portals (on dialogs) always worked for me in vue2
with portal-vue@2.1.7
ā I've used it heavily as part of my a11y-vue-dialog
plugin
I've recently migrated a big codebase to vue3
that made use of this feature and noticed that the same usages of the portal were now broken: dropdown menus with nested levels and nested dialogs stopped working (infinite loop).
Can this be considered a regression in vue3?
vue2/portal-vue@2
working finevue3/portal-vue@3.0.0
infinite looping[Vue warn]: Maximum recursive updates exceeded in component <portalTarget>.
As a workaround, from a quick test, the native vue3 Teleport
component seems to handle nesting without issues, but Teleport
still isn't a full drop-in replacement for portal-vue
yet (https://github.com/vuejs/core/issues/2015)
Having the same issue as @renatodeleao. Nesting dialogs used to work on vue 2, now it doesn't on vue 3, warning about infinite recursions. The native Teleport
doesn't work for me, as the portal target needs to be inside my app.
How can I have a portal inside a portal, both pointing to the same portal-target?
I'm using PortalVue to display all my modal dialogs in a top-level div to avoid z-ordering issues.
For this, I have a
my-dialog
component that defines a portal at its root:And a single
dialogs
portal-target:Many of my components "include" a dialog (e.g. for their related messages, etc.). This leads to the case where, sometimes, a dialog can include another dialog (e.g. when an action button in the top-level dialog defines its own nested success/failure dialogs as part of its template).
This leads to very weird behaviour in PortalVue. Sometimes, the component that defines the nested dialog will stop rendering when it gets opened. I created the following test case to try isolating the issue:
This led to the following error (and infinite recursion) whenever the inner dialog is opened:
How would you advise to setup PortalVue in this case?