1

I have list with an accordion, when you click on a item, all items opens, I need to open just one, I understand that a loop is needed to iterate over all the items and apply the class to a specific one, but how to do this, please help
component:

  <ul class="accordion accordion__trigger"
      :class="{'accordion__trigger_active': visible}"
      @click="open">
    <li class="accordion__item" v-for="category in MAIN_CATS">
        <nuxt-link exact no-prefetch active-class="link-active"
                   :to="`/category/${category.id}`"
                   class="menu-button">
          {{ category.title }}
        </nuxt-link>
        <div class="accordion__content">
          <div class="menu-sub-list" v-show="visible">
              <ul class="sub-list">
                <li class="menu-item"
                    v-for="sub in SUB_CATS(category.id)"
                    :key="sub.id">
                  <nuxt-link :to="`/category/${sub.id}`" class="menu-button">
                    {{ sub.title }}
                  </nuxt-link>
                </li>
              </ul>
          </div>
        </div>
    </li>
  </ul>

code:

name: "acc",
  data() {
    return {
      index: null,
      Accordion: {
        count: 0,
        active: null
      }
    };
  },
  computed: {
    ...mapGetters([
      'MAIN_CATS',
      'SUB_CATS'
    ]),
    visible() {
      return this.index === this.Accordion.active;
    }
  },
  methods: {
    ...mapActions([
      'GET_CATEGORIES_LIST',
    ]),
    open() {
      if (this.visible) {
        this.Accordion.active = null;
      } else {
        this.Accordion.active = this.index;
      }
    },
    start(el) {
      el.style.height = el.scrollHeight + "px";
    },
    end(el) {
      el.style.height = "";
    }
  },
  created() {
    this.index = this.Accordion.count++;
  },
  mounted() {
    this.GET_CATEGORIES_LIST()
  },

I have list with an accordion, when you click on a item, all items opens, I need to open just one, I understand that a loop is needed to iterate over all the items and apply the class to a specific one, but how to do this, please help

Alexander
  • 71
  • 7
  • 1
    Right now your click handler is on the whole `ul` element, so whatever in that you click it will trigger the method. Then, you simply set `this.index` as the active value. Looks like `index` is always null? Or how do you set it? Also, since visisble is a computed, it can only be true or false. But you need to have that value different for each item in the loop. Use a method instead that gets the current index and use that method in the loop, or better make a computed list and add a visible value to each entry. – Matthias S Aug 04 '20 at 16:24
  • did it like here https://stackoverflow.com/questions/59753165/toggle-only-accordion-clicked-vue-js – Alexander Aug 04 '20 at 18:13

1 Answers1

1

There are multiple differences between your code and code from the answer that you referred to.

You can notice that @click is placed in the same line as v-for.

The main reason for that is to be able to easily access index of each element in a loop.


Not to overcomplicate it for you, I created a basic use case scenario:

<template>
  <div id="accordion" class="accordion-container">
    <ul
      v-for="(category, index) in items"
      :key="index"
      class="accordion accordion__trigger"
      :class="{'accordion__trigger_active': visible===index}"
      @click="visible=index"
    >
      <li class="accordion__item">
        {{ category.title }}
        <div class="accordion__content">
          <div class="menu-sub-list" v-show="visible===index">
            <ul class="sub-list">
              <li class="menu-item">{{ category.sub }}</li>
            </ul>
          </div>
        </div>
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  name: "trial-page",
  data() {
    return {
      items: [
        {
          title: "Accordion 1",
          text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
          sub: "Pellentesque risus mi"
        },
        {
          title: "Accordion 2",
          text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
          sub: "Pellentesque risus mi"
        },
        {
          title: "Accordion 3",
          text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
          sub: "Pellentesque risus mi"
        }
      ],
      visible: null
    };
  }
};
</script>

<style>
.accordion__trigger_active {
  background-color: blue;
  color: white;
}
</style>

You can see that the idea is to operate with index value that is assigned to visible data property in this case.

We simply check if the visible is equal to the currently pressed item with the value of index.

With that we conditionally v-show element and trigger the class :class="{'accordion__trigger_active': visible===index}".


Note that if you had more v-for loops in the same component then you would need to make sure the value used for visible is always unique, for that you could simply add some string to it like:

@click="visible=index+'category'"


Also remember to assign a :key when using v-for.

Example: v-for="(category, index) in items" :key="index"

Jakub A Suplicki
  • 4,586
  • 1
  • 23
  • 31