'Group transition with dynamic list as a whole

I have a data_sets. I need to take out a certain dataset (data_source in the snipped), filter it (itemList in the snippet) and display it, with any simple group transition, preferably list transition in Vue's official guide


This is a snippet that I created on https://sfc.vuejs.org/ to play around.

<template>
  <div>
    <ul>
      <transition-group name="list" mode="out-in">
      <li v-for="item in itemList" :key="item.id">
        {{item.content}}
        </li>
      </transition-group>
    </ul>
  </div>
  
  <button @click="changeDatasource(0)">
    Dataset 1
  </button>
  <button @click="changeDatasource(1)">
    Dataset 2
  </button>
  
  <button @click="changeDatasource(2)">
    Dataset 3
  </button>
</template>

<script>
  export default {
    data() {
      return {
        data_sets: [
          [{
              id: 1,
              content: "Frog",
              active: 1,
            },
            {
              id: 2,
              content: "Elephant",
              active: 0,
            },
            {
              id: 3,
              content: "Racoon",
              active: 1,
            },
            {
              id: 8,
              content: "Cheetah",
              active: 1,
            },
          ],
          [{
              id: 4,
              content: "Rooster",
              active: 1,
            },
            {
              id: 5,
              content: "Cow",
              active: 1,
            },
            {
              id: 6,
              content: "Cat",
              active: 1,
            },
            {
              id: 7,
              content: "Dog",
              active: 0,
            },
          ],
          [{
              id: 10,
              content: "Lion",
              active: 1,
            },
            {
              id: 11,
              content: "Shark",
              active: 1,
            },
            {
              id: 12,
              content: "Bee",
              active: 0,
            },
            {
              id: 13,
              content: "Cockroaches",
              active: 0,
            },
          ],
        ],
        data_source: [],
      }
    },
    computed: {
      itemList: {
        get() {
          return this.data_source.filter((item) => item.active)
        },

        set(newValue) {
          //this.itemList = newValue; <--
        }
      }
    },
    methods: {
      changeDatasource: function(id) {
        //this.itemList = [] <--
        this.data_source = this.data_sets[id];
      }
    }
  }
</script>

<style scoped>
  .list-enter-active,
  .list-leave-active {
    transition: all 0.5s ease;
  }
  
  .list-enter-from,
  .list-leave-to {
    opacity: 0;
    transform: translateX(30px);
  }
</style>

When clicking on buttons to choose a data set, you can see the transition is not okay: old items still present when the new ones come in. The expected order should be: old items disappears, then new items come in.


It's very likely because itemList is replaced as a whole at a time. So I've tried to:

  1. Empty itemList = [],
  2. Continue to make the changes. (the commented part in the snippet)

RangeError: Maximum call stack size exceeded

Guess it bounds to some kind of infinite loop, at this point I'm completely pointless.

I've also messed around with transition mode, it isn't better.

My question is there any way to apply transition to list as a whole like this?



Solution 1:[1]

Solved it by working around with setTimeout and setInterval and changed from computed propery to watch. Content shifting is there but a little CSS and JavaScript manipulation will fix it. Not the best solution but by now that's what I can think of.

Result snippet

<template>
  <div>
    <ul>
      <transition-group name="list">
      <li v-for="item in itemList" :key="item.id">
        {{item.content}}
        </li>
      </transition-group>
    </ul>
  </div>
  
  <button @click="changeDatasource(0)">
    Dataset 1
  </button>
  <button @click="changeDatasource(1)">
    Dataset 2
  </button>
  
  <button @click="changeDatasource(2)">
    Dataset 3
  </button>
</template>

<script>
  export default {
    data() {
      return {
        data_sets: [
          [{
              id: 1,
              content: "Frog",
              active: 1,
            },
            {
              id: 2,
              content: "Elephant",
              active: 0,
            },
            {
              id: 3,
              content: "Racoon",
              active: 1,
            },
            {
              id: 8,
              content: "Cheetah",
              active: 1,
            },
          ],
          [{
              id: 4,
              content: "Rooster",
              active: 1,
            },
            {
              id: 5,
              content: "Cow",
              active: 1,
            },
            {
              id: 6,
              content: "Cat",
              active: 1,
            },
            {
              id: 7,
              content: "Dog",
              active: 0,
            },
          ],
          [{
              id: 10,
              content: "Lion",
              active: 1,
            },
            {
              id: 11,
              content: "Shark",
              active: 1,
            },
            {
              id: 12,
              content: "Bee",
              active: 0,
            },
            {
              id: 13,
              content: "Cockroaches",
              active: 0,
            },
          ],
        ],
        data_source: [],
        itemList: [],
      }
    },
    methods: {
      changeDatasource: function(id) {
        //this.itemList = [] <--
        this.data_source = this.data_sets[id];
      }
    },
    watch: {
      data_source: function(newValue, oldValue) {
        let initialLength = this.itemList.length;
        if (initialLength > 0) {
          this.itemList = [];
        }
        let dataLength = newValue.length;
        let interval = 200;
        let i = 0;
        let timer = setInterval(() => {
          setTimeout(() => {
            if (typeof newValue[i - 1] != 'undefined') {
              this.itemList.push(newValue[i - 1])
            }
          }, interval)
          if (i === dataLength - 1) {
            clearInterval(timer);
          }
          i++;

        }, interval)
      }
    }
  }
</script>

<style scoped>
  .list-enter-active,
  .list-leave-active {
    transition: all 0.5s ease;
  }
  
  .list-enter-from,
  .list-leave-to {
    opacity: 0;
    transform: translateX(30px);
  }
</style>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Huy Ph?m