Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vuejs - Accordion

I'm trying to create an accordion using vuejs.

I found some examples online, but what I want is different. For SEO purpose I use "is" and "inline-template", so the accordion is kind of static not fully created in Vuejs.

I have 2 problems/questions:

1) I need to add a class "is-active" on the component based on user interaction(clicks), because of this I receive the following error.

Property or method "contentVisible" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option.

This probable because I need to set it at instance level. But "contentVisible" have a value (true or false) different for each component.

So I thought using at instance level an array of "contentVisible" and a props (pass thru instance) and custom events on child to update the instance values.

2) Could work but it is a static array. How can I make a dynamic array (not knowing the number of item components) ?

<div class="accordion">
    <div>
        <div class="accordion-item" is="item"  inline-template :class="{ 'is-active':  contentVisible}" >
            <div>
                <a @click="toggle" class="accordion-title"> Title A1</a>
                <div v-show="contentVisible" class="accordion-content">albatros</div>
            </div>
        </div>
        <div class="accordion-item" is="item"  inline-template :class="{ 'is-active': contentVisible}" >
            <div>
                <a @click="toggle" class="accordion-title"> Title A2</a>
                <div v-show="contentVisible" class="accordion-content">lorem ipsum</div>
            </div>
        </div>

    </div>

var item = {
  data: function() {
      return {
          contentVisible: true
      }
  },

  methods: {
      toggle: function(){
          this.contentVisible = !this.contentVisible
      }
  }
}

new Vue({
    el:'.accordion',
    components: {
        'item': item
    }
})

Update I create the following code but the custom event to send the modification from component to instance is not working, tabsactive is not changing

var item = {
  props: ['active'],
  data: function() {
      return {
          contentVisible: false
      }
  },
  methods: {
      toggle: function(index){
          this.contentVisible = !this.contentVisible;
          this.active[index] = this.contentVisible;
          **this.$emit('tabisactive', this.active);**
          console.log(this.active);
      }
  }
}

new Vue({
    el:'.accordion',
    data: {
      tabsactive: [false, false]
    },
    components: {
        'item': item
    }
})

<div class="accordion" **@tabisactive="tabsactive = $event"**>
        <div class="accordion-item" is="item"  inline-template :active="tabsactive" :class="{'is-active': tabsactive[0]}">
            <div>
                <a @click="toggle(0)" class="accordion-title"> Title A1</a>
                <div v-show="contentVisible" class="accordion-content">albatros</div>
            </div>
        </div>
        <div class="accordion-item" is="item"  inline-template :active="tabsactive" :class="{'is-active': tabsactive[1]}">
            <div>
                <a @click="toggle(1)" class="accordion-title" > Title A2</a>
                <div v-show="contentVisible" class="accordion-content">lorem ipsum</div>
            </div>
        </div>
</div>
like image 715
user3541631 Avatar asked Dec 14 '16 11:12

user3541631


2 Answers

On point 1:

You have to define contentVisible as a vue instance variable, as you have accessed it with vue directive v-show, it searches this in vue data, watchers, methods, etc, and if it does not find any reference, it throws this error.

As your accordion element is associated with the parent component, you may have to add contentVisible data there, like following:

new Vue({
    el:'.accordion',
    data: {
       contentVisible: true
    }
    components: {
        'item': item
    }
})

If you have multiple items, you may use some other technique to show one of them, like have a data variable visibleItemIndex which can change from 1 to n-1, where n is number of items.

In that case, you will have v-show="visibleItemIndex == currentIndex" in the HTML.

You can as well have hash for saving which index are to de displayed and which to be collapsed.

On point 2:

You can use v-for if you have dynamic arrays. you can see the documentation here.

like image 156
Saurabh Avatar answered Nov 15 '22 06:11

Saurabh


This works for me:

<template>
    <div>
        <ul>
            <li v-for="index in list" :key="index._id">

                <button @click="contentVisible === index._id ? contentVisible = false : contentVisible = index._id">{{ index.title }}</button>

                <p v-if='contentVisible === index._id'>{{ index.item }}</p>

            </li>
        </ul>
    </div>
</template>

<script>
    export default {
        name: "sameName",
        data() {
            return {
                contentVisible: false,
                list: [
                    {
                    _id: id1,
                    title: title1,
                    item: item1
                    },
                    {
                    _id: id2,
                    title: title2,
                    item: item2
                    }
                ]
            };
        },
    };
</script>
like image 31
Ismael Soschinski Avatar answered Nov 15 '22 07:11

Ismael Soschinski