Vue: Strange variable or v-show behavior within <transition-group> with component and vue-resource involved

Created on 12 May 2017  路  11Comments  路  Source: vuejs/vue

Version

2.3.3

Reproduction link

https://jsfiddle.net/50wL7mdz/33234/

Steps to reproduce

Run JSfiddle several times.

There are two <child> components in source, displaying same content after http call done asynchronously.
One component is wrapped within <transition-group>, while other is not.

What is expected?

Both <child> components to display "I'm visible" when loaded.

What is actually happening?

First component version within <transition-group> shows randomly.
<div v-show="!loading1" key="content"> with loading1 = false still produces style="display:none" on element


It's strange, but it's happens only with conjunction of vue-resource, and styles in <transition-group>
In other cases everything works well

Most helpful comment

Ok guys, we still didn't find no working answer, but issue is closed.

Let me summarize once again.

I try to make a DRY solution to use different components in <route-view> and show spinner/loader within transition during component switch and some initialization.

What was tried:

First:

<transition-group name="fade" appear mode="out-in">
  <div v-show="loading1" key="loader">Loading</div>
  <div v-show="!loading1" key="content">
    <child v-on:loaded="loading1 = false"></child>
  </div>
</transition-group>

The problem: transition applies only on top wrapper DIV element but doesn't apply when I need to hide first DIV (loader) and show DIV with child component.

Second:

<transition-group name="fade" appear mode="out-in">
  <div v-show="loading1" key="loader">Loading</div>
  <div v-show="!loading1" key="content">
    <child v-on:loaded="loading1 = false"></child>
  </div>
</transition-group>

The problem: Both blocks get display: none randomly at the same time so both blocks ar not shown

Third:

  <div v-show="loading1" key="loader">Loading</div>
  <div v-show="!loading1">
    <child v-on:loaded="loading1 = false"></child>
  </div>

The problem: No smooth transition. I'd like to switch blocks smoothly.

Fourth:
Same as above, but move loader inside component.

The problem: 1. Not DRY. 2. when component switches, the loader switches as well and thus the visually twitches.

The nearest solution to what is needed is Second:. But random display: none when it must be shown is unexpected, thus I reported it as a bug.

So how to achieve needed result with Vue 2 guys? :)

All 11 comments

Make sure to use different key attrs for the components, though. Your problem appears when the second child appears before the first one

key has nothing to do with it, as second component is not in <transition-group>.

However I've updated JSfiddle for your convenince and removed key from second component. Problem remains the same.

https://jsfiddle.net/50wL7mdz/33234/

Please reopen the issue.

For the sake of clarity it doesn't work as expected when only one component wrapped in <transition-group> exists on the page.

I've added second one just to illustrate the difference in result

You should be using <transition> instead of <transition-group>. The latter does not work with v-show.

@yyx990803 thanks for joining. Didn't know about that. Maybe it should be mentioned in docs. :).

However:

  1. It actually works in most (except some rare) cases. Is there any type of race condition happening? For example it works well if I emit from child component on setTimeout(), rather than from http request callback.
  1. In real prodution code I used <transition-group> because I need to show a loader in a separate div, while I wait the content in child component to load. Then transition should smoothly switch them between. For example:
<transition-group name="fade" appear mode="out-in">
  <div v-show="loading1" key="loader">Loading</div>
  <div v-show="!loading1" key="content">
    <child v-on:loaded="loading1 = false"></child>
  </div>
</transition-group>

However If I turn this into

<transition name="fade" appear mode="out-in">
  <div>
    <div v-show="loading1">Loading</div>
    <div v-show="!loading1">
      <child v-on:loaded="loading1 = false"></child>
    </div>
  </div>
</transition>

... transition applies only on top wrapper DIV element but doesn't apply when I need to hide first DIV (loader) and show DIV with child component.

You may see it in another Fiddle:
https://jsfiddle.net/50wL7mdz/33238/

I also cannot do this without wrapping DIV like this:

<transition name="fade" appear mode="out-in">
  <div v-show="loading1">Loading</div>
  <div v-show="!loading1">
    <child v-on:loaded="loading1 = false"></child>
  </div>
</transition>

As I get warning: <transition> can only be used on a single element. Use <transition-group> for lists.

So whatever option I take I hit the wall.

It would probably be better to approach this problem in a different way.

If you want to show a loader in place of where a component is still loading some data, I would recommend using a directive on the component that attaches a loader on a certain condition (i.e. v-loading="myComponentIsLoading"

Another approach would be to use a dynamic <component :is="someComponent"> approach: https://jsfiddle.net/50wL7mdz/33241/

@TheDutchCoder:
Dynamic component approach doesn't work as child component which tells parent about it has loaded data _must_ be mounted to start loading data. Thus I use v-show, not v-if. (it's even more complicated, as this child component in a real app is actually a <route-view>). You can check in your JSfiddle, if you try to modify .comp var from my-thing, rather than $root.

I didn't get the idea about custom directive well. Could you elaborate? Do you mean to tell the component to show loaded inside it and not in parent? If so, then this is actually what I'm trying to resolve. As there are many <route-view>'s each having it's own loader, and I'm trying to make DRY code and make one global loader which shows when each route initializes itself and loads. And it works ok, until I try to add smooth transitions.

Ok guys, we still didn't find no working answer, but issue is closed.

Let me summarize once again.

I try to make a DRY solution to use different components in <route-view> and show spinner/loader within transition during component switch and some initialization.

What was tried:

First:

<transition-group name="fade" appear mode="out-in">
  <div v-show="loading1" key="loader">Loading</div>
  <div v-show="!loading1" key="content">
    <child v-on:loaded="loading1 = false"></child>
  </div>
</transition-group>

The problem: transition applies only on top wrapper DIV element but doesn't apply when I need to hide first DIV (loader) and show DIV with child component.

Second:

<transition-group name="fade" appear mode="out-in">
  <div v-show="loading1" key="loader">Loading</div>
  <div v-show="!loading1" key="content">
    <child v-on:loaded="loading1 = false"></child>
  </div>
</transition-group>

The problem: Both blocks get display: none randomly at the same time so both blocks ar not shown

Third:

  <div v-show="loading1" key="loader">Loading</div>
  <div v-show="!loading1">
    <child v-on:loaded="loading1 = false"></child>
  </div>

The problem: No smooth transition. I'd like to switch blocks smoothly.

Fourth:
Same as above, but move loader inside component.

The problem: 1. Not DRY. 2. when component switches, the loader switches as well and thus the visually twitches.

The nearest solution to what is needed is Second:. But random display: none when it must be shown is unexpected, thus I reported it as a bug.

So how to achieve needed result with Vue 2 guys? :)

To solve this, use v-if instead of v-show.

Also, to avoid warning, you cannot use v-if="condition" v-else,

Instead, use v-if="condition" v-if="!condition".

This works for me, I hope this will be helpful to you

@Heleninsa unfortunately v-if doesn't allow a component inside it to initialize. So you cannot track if it's loaded or not. It was mentioned somewhere in previous comments.

nobody cares. v-if doesn't work for me

Was this page helpful?
0 / 5 - 0 ratings

Related issues

franciscolourenco picture franciscolourenco  路  3Comments

loki0609 picture loki0609  路  3Comments

bfis picture bfis  路  3Comments

paceband picture paceband  路  3Comments

aviggngyv picture aviggngyv  路  3Comments