javascriptvue.jsvuejs3v-for

Use a function in a v-for loop


I am trying to create a menu on Vue 3 with categories and subcategories. The goal is that when I click on a category, the subcategories specific to that category will appear below and be removed when I click again. The problem is that I can't get the function to be specific to each category. Whenever I click on a category, the subcategories of all categories are displayed.

I reproduced my code in a simpler example.

JS :

const menu = [
  {
    label: "Category 1",
    key: "category_1",
    subcategories: [
      {
        label: "1 - Subcategory 1",
        key: "1_subcategory_1",
      },
      {
        label: "1 - Subcategory 2",
        key: "1_subcategory_2",
      },
    ],
  },
  {
    label: "Category 2",
    key: "category_2",
    children: [
      {
        label: "2 - Subcategory 1",
        key: "2_subcategory_1",
      },
    ],
  },
];

const isDisplay = ref(false);

const showSubcategories = () => {
  isDisplay.value = !isDisplay.value
}

VUE :

<template>
  <div v-for="category in menu" :key="category.key">
    <div @click="showSubcategories()">
      <span> {{ category.label }}</span>
    </div>
    <div v-if="isDisplay">
      <div
        v-for="subcategory in category.subcategories"
        :key="subcategory.key"
      >
        {{ subcategory.label }}
      </div>
    </div>
  </div>
</template>

I suspect that I have to add an index (like showSubcategories(category.key))or pass one or more parameters to my function but despite my attempts, I can't solve the problem. Thanks in advance.


Solution

  • You have a few options to do this. You can add additional property to each category to track if it is open or not, or you can have a separate variable to store IDs of open categories.

    Here is example with a separate variable to store IDs of open categories:

    const menu = [
      {
        label: "Category 1",
        key: "category_1",
        subcategories: [
          {
            label: "1 - Subcategory 1",
            key: "1_subcategory_1",
          },
          {
            label: "1 - Subcategory 2",
            key: "1_subcategory_2",
          },
        ],
      },
      {
        label: "Category 2",
        key: "category_2",
        subcategories: [
          {
            label: "2 - Subcategory 1",
            key: "2_subcategory_1",
          },
        ],
      },
    ];
    
    const openCategories = ref([]);
    
    const toggleSubcategories = (categoryId) => {
      if(openCategories.value.includes(categoryId)) {            
        openCategories.value.splice(openCategories.value.indexOf(categoryId), 1);
      } else {
        openCategories.value.push(categoryId);
      }
    }
    
    <template>
      <div v-for="category in menu" :key="category.key">
        <div @click="toggleSubcategories(category.key)">
          <span> {{ category.label }}</span>
        </div>
        <div v-if="openCategories.includes(category.key)">
          <div
            v-for="subcategory in category.subcategories"
            :key="subcategory.key"
          >
            {{ subcategory.label }}
          </div>
        </div>
      </div>
    </template>