Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Components transition enter function not called when using Vue version 2.6.10 #10227

Closed
katerlouis opened this issue Jul 3, 2019 · 10 comments
Closed

Comments

@katerlouis
Copy link

Version

2.6.10

Reproduction link

https://github.com/katerlouis/vue-2610-bug

Steps to reproduce

serve or build the app
and press the toggle button

What is expected?

HelloWorld component should call enter function

What is actually happening?

HelloWorld component doesn't call enter function


enter of the transition directly inside App.vue (the logo img) gets called though, which is odd.
With vue v2.5.17 it works as expected (haven't used any other vue version since)

@posva
Copy link
Member

posva commented Jul 3, 2019

You need to use the appear prop for the animation to play initially

@posva posva closed this as completed Jul 3, 2019
@katerlouis
Copy link
Author

katerlouis commented Jul 3, 2019

I didn't say anything about initial render is an issue- if you press the toggle button multiple times, the enter function of HelloWorld still isn't called with Vue 2.6.10, as you can see in the provided demo. Please reopen the issue @posva

@kanbekotori
Copy link

I have the same problem, and i down to 2.6.8 to get work.

@posva
Copy link
Member

posva commented Jul 4, 2019

the transition inside HelloWorld needs the prop appear to play at the same time as the parent appears:

  <transition :css="false" @enter="enter" @leave="leave" appear>
    <div class="hello">

For the leave transition it's currently not possible, you can track the feature at #9328

I hope this helps :)

@katerlouis
Copy link
Author

katerlouis commented Jul 5, 2019

Wait? Doesn't this also mean the HelloWorld component would animate / call the enter function on first load? I understand that this is the purpose of the appear-prop. What if I don't want that?

And the leave transition isn't the issue here. It works as expected. Before the component dismounts, its leave-function gets called.

I'm wondering why this change was introduced. We clearly lose functionality here.

@katerlouis
Copy link
Author

katerlouis commented Jul 10, 2019

@posva could you please look into this again? I'm still under the impression there is a misunderstanding here.

Again: leave is not an issue.
And initial animation is not desired.

I still do not understand why the enter function should be called in this situation

// App.vue
<transition @enter="enter" @leave="leave" :css="false">
	<img alt="Vue logo" src="./assets/logo.png" v-if="show">
</transition>

<HelloWorld msg="Helloooo" />

but not here

// HelloWorld.vue
<transition :css="false" @enter="enter" @leave="leave"> 
	<div class="hello">
		<h1>{{ msg }}</h1>
                ....
	</div>
</transition>

It's hard to believe that this is intended.
Could you please help me understand?

@posva
Copy link
Member

posva commented Jul 10, 2019

It is intended because the first one is wrapping an element with a v-if while the second isn't and it's directly there when the parent renders. So if you want it to apply the enter transition, you need the appear prop

@katerlouis
Copy link
Author

katerlouis commented Jul 11, 2019

Okay, if I add appear to the transition inside HelloWorld.vue, its enter-function rightfully/expectedly gets called when toggling. Cool.

But it also gets called on initial load of the app, which is not what I want and what I understand is exactly what appear is there for.
How do you suggest working around that?
Because with this change, as is, I cannot update to the newer vue version in my app.

I read what you are saying, but still not fully understand what you are trying to solve with this new implementation. What was wrong with the way it worked before? Maybe an example would help illustrating the problem the old implementation caused.

@LinusBorg
Copy link
Member

LinusBorg commented Jul 11, 2019

The way it works now it the way it was always intended to work.

Bug Report: #9628
PR that fixed it: #9668

  • Without appear, elements are not transitioned in on the first render of the <transition> component(!), only on updates of the transition component (i.e. by switching a v-if inside of the <transition> component
  • With appear, they are transitioned in on first render as well.

But it also gets called on initial load of the app which is not what I want [...]

That could only be the case if the v-if="show" in App.vue were also true when loading the app, which it isn't in your example.

So I assume it is (or can be) true at the initial load of your real app?

How do you suggest working around that?

I would handle this in a global fashion, i.e. set a (non-reactive) flag in the main component to trueafter initial render.

Then each component can check that flag on re-render and determine wether or not appear should be applied.

in App.vue

provide() {
  return {
    isFirstRender: () => { return this.isFirstRender === false }
  }
},
updated() {
  this.isFirstRender = false // do *not* add this prop to `data`!
}

in HelloWorld.vue

<template>
  <transition
    :appear="!isFirstRender()"
    :css="false"
    @enter="enter"
    @leave="leave"
  >
    <div class="hello">
      xxx
    </div>
  </transition>
</template>

<script>
import Vue from 'vue'
import { TweenMax } from 'gsap'
export default Vue.extend({
  name: 'HelloWorld',
  props: {
    msg: String,
  },
  inject: ['isFirstRender'],
  methods: {
    enter(el, done) {
      console.log('enter hello world called', el)
      TweenMax.from(el, 2, { opacity: 0, xPercent: -100, onComplete: done })
    },
    leave(el, done) {
      console.log('leave hello world called', el)
      TweenMax.to(el, 2, { opacity: 0, xPercent: -100, onComplete: done })
    },
  },
})
</script>

I used provide/inject so this can be re-used in all of the app in different places that need this.

Other patterns, i.e. with HelloWorld accepting a prop, are also possible to imagine.

@katerlouis
Copy link
Author

katerlouis commented Nov 5, 2019

@LinusBorg I think I found a slight thought error in your code.
The pattern works really great, but on initial load your provided function, which asks "Am I the first render?!" returns false, since the flag is still undefined and not false yet;

So if I'm correct the pattern should look like this:

provide() {
  return {
    isFirstRender: () => { return this.isFirstRender === undefined }
  }
},
updated() {
  this.isFirstRender = false // do *not* add this prop to `data`!
}

Atleast that's how it worked for me; now !isFirstRender() is false on first render, as expected;

[EDIT]
There's another issue I found with this pattern; you set the initalRender status only if some updated fires, and that only that only happens when data changes. That's not always the case. So I had to do this.$forceUpdate() in App.vues mounted() .. or you could just set this.isFirstRender = false in mounted() ..

Here's what I don't understand:
After doing this.isFirstRender = false in App.vues mounted()-hook, the slideOver component still doesn't slide on initial render (which is what I want ..) – but the App.vue mounts earlier than the slide component does, so why is this.isfirstRender still undefined by that time?

[EDIT 2]
image
Why on earth is mounted() of the SlideOver fired before Apps mounted()?!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants