How to alternate sibling Vue component v-for renderings for a shared array in a list in the parent component

Viewed 140

I have the following challenge to work on and thanks for any input:

From 'App' use 'TestListItem' when an item index is odd and 'TestListBetterItem' when item index is even. Must use the same index for both components.

I have tried setting the keys for my two separate child components as being equal to odd and the other to even but I receive a duplicate key error. I need to pass in alternating values from two child components into a shared parent component. The full code is below:

App.vue

    <template>
  <div id="app">
    <test-list-item>
    </test-list-item>
    <test-list-better-item>     
    </test-list-better-item>
  </div>
</template>

          <!-- <template #title>
            I AM A HEADER
          </template> -->
          <!-- <template #footer>
          I AM A FOOTER
          </template> -->

<script>
import { TestBetterList, TestListItem, TestListBetterItem } from './components';


export default {
  name: 'App',
  components: { TestBetterList, TestListItem, TestListBetterItem },
  data() {
    return {
      items: ['better-item-1',  'better-item-2', 'better-item-3']
    };
  },
  //   watch: {
  //       'better-item-1': {
  //     handler: 'oddMethod',
  //     immediate: true,
  //     deep:true
  //   },
  //       'better-item-2': {
  //     handler: 'evenMethod',
  //     immediate: true,
  //     deep: true
  //   },
  //       'better-item-3': {
  //     handler: 'oddMethod',
  //     immediate: true,
  //     deep: true
  //   },
  // },
  methods: {

  }
};
</script>

<style lang="scss">
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

TestListItem.vue

    <template>
  <div class="test-list-item">
        <template v-for="(item, index) in items">
      <slot>
        <div :key="index">{{ item }}</div>
      </slot>
    </template>
  </div>
</template>

<script>

export default {
  name: 'test-list-item',
  props: {

  },
  data() {
    return {
      items: ['better-item-1',  'better-item-2', 'better-item-3']
    };
  },
  computed: {
   
},
  mounted() {
  },
  created() {},

    //   methods: {
    //     oddMethod: function () {
    //     items[0]
    //   }
    // }

};
</script>

<style scoped lang="scss">
.test-list-item {
  color: purple
}


</style>

TestListBetterItem.vue

    <template>
  <div class="test-list-better-item">
    <template v-for="(item, index) in items">
      <slot>
        <div :key="index">{{ item }}</div>
      </slot>
    </template>

  </div>
</template>

<script>

export default {
  name: 'test-list-better-item',
  props: {

  },
  data() {
    return {
      items: ['better-item-1',  'better-item-2', 'better-item-3']
    };
  },
  computed: {
   
},
  mounted() {
  },
  created() {},

    //   methods: {
    //     evenMethod: function () {
    //     items[0]
    //   }
    // }

};
</script>

<style scoped lang="scss">
.test-list-better-item {
  color: rgb(255, 145, 0)
}


</style>
1 Answers

I might be misunderstanding the task you're facing, but it seems to me it's about the conditional rendering of two different types of list items, not about the creation of two separate lists. So when it refers to TestListItem and TestListBetterItem those are components representing list items, and the actual array containing the list is still in the App component. So I'd first create two separate components by these names, and then apply the v-if and v-else directives to choose which of those to render based on the index of the list item being rendered.

<template> 
  <div id="app"> 
    <ul id="test-list"> 
      <li v-for="(listItem,listItemIndex) in testList"> 
         <TestListItem v-if="listItemIndex % 2 === 0" /> 
         <TestListBetterItem v-else /> 
      </li> 
    </test-list> 
  </div> 
</template> 

I created a StackBlitz for you ( although I used Vue 3 for it, but you can migrate it easily back to Vue 2 ):

CODE HERE

Related