2

I'm trying to figure how to make each of my items listed, independently be hidden and show, currently when you open one the other opens as well. Would I need to use an index of some sort? I am using v-show because I need the content to be rendered before hand and hidden on load but shown on click.

<template>
    <div>
        <div v-for="item in items" :key="item">
            {{ item.title }}

            <div v-if="item.examples != null">
                <a v-on:click="visibleExample = !visibleExample">Example</a>
                <transition name="fade">
                    <div v-show="visibleExample">
                        <div v-for="example in item.examples" :key="example">
                            {{ example }}
                        </div>
                    </div>
                </transition>
            </div>
        </div>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                visibleExample: false,
                items: [{
                        title: 'Title',
                        examples: [
                            'Question',
                            'Answer'
                        ]
                    },
                    {
                        title: 'ABC',
                        examples: [
                            '1',
                            '2',
                            '3',
                            '4',
                            '5'
                        ]
                    }
                ]
            }
        }
    }
</script>

Please note: This is a stripped version of my original code for simplicity.

3 Answers 3

2

You could to use a new aux list of items and there can append a new boolean property for each object of item list. Then you could use auxItems on your v-for as below:

Vue.config.devtools = false
Vue.config.productionTip = false
new Vue({
  el: "#app",
  data() {
    return {
      items: [
        {
          title: 'Title',
          examples: [
            'Question',
            'Answer'
          ]
        },
        {
          title: 'ABC',
          examples: [
            '1',
            '2',
            '3',
            '4',
            '5'
          ]
        }
    	],
      auxItems: []
    }
  },
  mounted () {
    this.auxItems = this.items.map(i => ({ ...i, isVisible: false }))
  }
})
.item {
  border: 1px solid black
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <div v-for="item in auxItems" :key="item.title" class="item">
    {{ item.title }}
    <div v-if="item.examples !== null">
      <a v-on:click="item.isVisible = !item.isVisible">Example</a>
      <transition name="fade">
        <div v-show="item.isVisible">
          <div v-for="example in item.examples" :key="example">
            {{ example }}
          </div>
        </div>
      </transition>
    </div>
  </div>
</div>

Sign up to request clarification or add additional context in comments.

2 Comments

I like your idea, I tried doing it this way: jsfiddle.net/MrCarrasco/doqfbts6 but got lost in between. The Jsfiddle currently only shows the items but not independently. My goal was to try to keep this as clean as possible. I will change this into vue component form and bundle with webpack if I get this to work later.
looks well but you should iterate the property item.examples with v-for inside of the child component.
1

You've got three main options:

  1. Use an array of booleans for visibleExample, paired up with your items by index. The pairing could either be performed in the template using an index from the v-for or the two arrays could be 'merged' using a computed property.
  2. Add a boolean property to each of your items that holds whether or not the example should be shown for that particular item.
  3. Extract a separate component to represent an item. This could include everything in the v-for or just the example portion. Either way there'd be a visibleExample property in the data, with each component holding its own state. This is often the best solution but it can get fiddly if you need the visibleExample state outside the component. For the code in the question this wouldn't be a problem but it would depend on what the full code looked like.

2 Comments

Which would be the simplest option to implement? 3, a separate component?
@Charles There isn't really enough information in the question to answer that. Any of them could be implemented in under 5 minutes. I would suggest trying all of them if you aren't clear exactly what each entails. Option 2 is probably the simplest given the code in the question but in practice it might involve combining server-side data and UI state in one data structure, which can make it fiddly. The component boundary introduced by option 3 is useful to solve this problem but it can be an obstacle if data subsequently needs passing across it.
1

As you suggested, one way to solve it is to keep track of the current selected item.

This can be achieved by doing this:

<div v-if="item.examples != null">
   <a v-on:click="visibleExample = !visibleExample; crtSelectedItem = item">Example</a>
   <transition name="fade">
    <div v-show="visibleExample && item.id === crtSelectedItem.id">
        <div v-for="example in item.examples" :key="example">
           {{ example }}
        </div>
     </div>
   </transition>
 </div>

1 Comment

I tried this word for word and the result I need was not achieved.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.