LinusBorg / portal-vue

A feature-rich Portal Plugin for Vue 3, for rendering DOM outside of a component, anywhere in your app or the entire document. (Vue 2 version: v2.portal-vue.linusb.org)
http://portal-vue.linusb.org
MIT License
3.89k stars 188 forks source link

change event is incorrectly triggered #223

Closed nekuz0r closed 1 year ago

nekuz0r commented 5 years ago

The change event is triggered when a portal-target is registered/unregistered.

The portal-target component watches the transports object and anytime the content of it changes then the change event is triggered even though the content of the portal-target did not changed.

I expect it to be called only when the content is changed.

Changing the watch function @ https://github.com/LinusBorg/portal-vue/blob/develop/src/components/portal-target.tsx#L33 by the following seems to fix the issue:

ownTransports(newOwnTransports, oldOwnTransports) {
      if (newOwnTransports.length === oldOwnTransports.length) {
          return;
      }
      this.$emit('change', this.children().length > 0)
},
LinusBorg commented 5 years ago

I don't see this as a bug. Whenever the source <portal> sends new vnodes, the content changes. Wether or not that new content actually looks like the old content which means nothing changed for the consumer is not taken into consideration.

Doing that would require to do a complete diff of he virtualDOM that the source <portal> component(s) provided, which isn't really warranted considering performance implications.

What you are suggesting is a bit of a different feature. you want the event to only emit when the boolean value that it comes with actually flips. It wasn't intended to work that way before, but may be worth considering.

Changing this behaviour would constitute a breaking change though, strictly speaking

tmorehouse commented 5 years ago

what about a new event targets-changed (or some better name)?

ggedde commented 4 years ago

I am not sure if this is related to this issue or if I should create my own, but the issue I am getting is whenever my v-model changes that is outside the portal the portal emits a change event. So on a text input this is happening on keyup so every keystroke is emitting the change event.

I am not sure why the portal would need to emit a change if it is not using any reactive objects. It seems to me that it should only emit the change if the reactive objects get modified.

Here is a simple child component example:

<template>
    <section class="dashboard">
        <portal to="toolbar">
            <h1>Dashboard</h1>
        </portal>
        <input type="text" v-model="foo" />
    </section>
</template>
LinusBorg commented 4 years ago

The re-render of your components means that the portal receives updated slot content. The fact that this content is semantically the same doesn't matter - its new vnodes. That's how slots work, unrelated to portal-vue.

So the portal updates, which means the portal-target updates.

I think you should be able to prevent this from happening by using a scoped slot with the newer v-slot syntax, since Vue will cache the scoped-slot function that's being generated:

<template>
    <section class="dashboard">
        <portal to="toolbar" v-slot:default>
            <h1>Dashboard</h1>
        </portal>
        <input type="text" v-model="foo" />
    </section>
</template>