Vuejs - Expanding Panels

Trying to implement an accordion using Vue.js has been a bit challenging for me.

While I came across some examples online, my requirements are slightly different. In order to maintain SEO compatibility, I need to use "is" and "inline-template", which makes the accordion relatively static rather than fully powered by Vue.js.

I have encountered two main issues/questions:

1) The need to dynamically add the class "is-active" to the component based on user interactions (clicks), leading to the following error message.

The property or method "contentVisible" is not defined within the instance but referenced during rendering. It's essential to declare reactive data properties within the data option.

This issue arises because setting it at the instance level requires having a value (true or false) specific to each component for "contentVisible".

To tackle this, I thought of utilizing an array of "contentVisible" at the instance level along with props being passed through instances and custom events on children to update the values at the instance level.

2) While this approach could work, managing a static array poses limitations. How can I create a dynamic array without 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 implemented the code below, however, the custom event that is supposed to send modifications from the component to the instance isn't functioning properly as tabsactive remains unchanged.

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>

Answer №1

Here is a solution that I found effective:

<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>

Answer №2

Regarding point 1:

To resolve the issue, ensure that you have defined contentVisible as a vue instance variable. This is necessary because when using the vue directive v-show, Vue searches for this data in its data objects, computed properties, methods, etc. If it cannot find a reference to it, an error will be thrown.

If your accordion element is linked to the parent component, make sure to add the contentVisible data there. You can achieve this by adding the following code snippet:

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

In case of multiple items, you can use another method to display one at a time. For example, you can create a data variable named visibleItemIndex which can be toggled from 1 to n-1 (where n is the number of items).

In such scenarios, you would use

v-show="visibleItemIndex == currentIndex"
in the HTML markup.

You may also consider utilizing a hash to store the indices to be displayed and collapsed.

As for point 2:

If you are working with dynamic arrays, you can leverage v-for. Refer to the official documentation for more details.

Answer №3

I am struggling to comprehend what it is that you are looking for and the reason behind why you want it, but I believe this solution may meet your needs?

Vue.component('accordion-item', {
  template: '#accordion-item',
  methods: {
    toggle() {
      if(this.contentVisible){
      return
      }
      if(this.$parent.activeTab.length >= 2){
      this.$parent.activeTab.shift()
      }
      this.$parent.activeTab.push(this)
    }
  },
  computed: {
    contentVisible() {
      return this.$parent.activeTab.some(c => c === this)
    }
  }
})

const Accordion = Vue.extend({
  data() {
    return {
      activeTab: []
    }
  },
  methods: {
    handleToggle($event) {
      this.activeTab = []
    }
  }
})

document.querySelectorAll('.accordion').forEach(el => new Accordion().$mount(el))
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>

<template id="accordion-item">
  <div class="accordion-item" :class="{ 'is-active':  contentVisible}">
      <a href="#" @click="toggle" class="accordion-title"><slot name="title"></slot></a>
      <div v-show="contentVisible" class="accordion-content" @click="$emit('toggle', $event)">
        <slot name="content"></slot>
      </div>
  </div>
</template>

  <div class="accordion">
    <accordion-item @toggle="handleToggle">
      <p slot="title">a title</p>
      <p slot="content">there are words here</p>
    </accordion-item>
    <accordion-item @toggle="handleToggle">
      <p slot="title">titles are for clicking</p>
      <p slot="content">you can also click on the words</p>
    </accordion-item>
    <accordion-item @toggle="handleToggle">
      <p slot="title">and another</p>
      <p slot="content">only two open at a time!</p>
    </accordion-item>
    <accordion-item @toggle="handleToggle">
      <p slot="title">and #4</p>
      <p slot="content">amazing</p>
    </accordion-item>
  </div>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The use of the "declare" keyword is prohibited within the `script setup` section of Vue

I need to integrate the function getCookie into my Vue file. This function is already defined in the HTML file where the Vue file will be injected. <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" ...

Tab knockout binding

I have a section in my HTML with 2 tabs. The default tab is working properly, but when I attempt to switch to the other tab, I encounter an error. Can anyone provide assistance in determining why this error occurs? Here is the HTML code: <ul class="na ...

Steer clear of displaying the latest model directly

Currently, I have a form for creating a new Model named Route. This form includes a select field called takeover, which displays all existing Routes for the user to choose from and establish a relationship with the selected Route. The issue I am facing is ...

Error: The property 'length' cannot be read from an undefined parent causing Uncaught TypeError

Hey there, take a look at this cool stuff http://jsfiddle.net/J9Tza/ <form class="validation"> <div> <input type="email" class="form-control" id="inputEmail" name="email" placeholder="Email" pattern=".{3,200}" title="3 to 200 characters" r ...

What is the correct way to establish a Cookie (header) using XMLHttpRequest in JavaScript?

I am attempting to set a cookie in an XSS request using XMLHttpRequest. After reviewing the XMLHttpRequest Specification, I discovered that section 4.6.2-5 indicates that setting certain headers like Cookie and Cookie2 may not be allowed. However, I am lo ...

Maximizing the Use of Multiple Conditions for Styling in AngularJS

I have a table where I need to set different colors based on the values in one of the columns. I've successfully managed to set two colors using NgClass, but I'm unsure how to set up three different conditions. <scri ...

What is the best way to prevent images from being loaded with broken links?

Currently, I am working on a new feature that involves rendering images from servers. In the process of aligning these images, I noticed an excessive amount of white space due to loading images with broken links. https://i.stack.imgur.com/jO8BR.png Here ...

Is it possible to execute a program on MacOS through a local HTML website?

Are there any straightforward methods to launch Mac programs using HTML? I've created an HTML page featuring a text field and several buttons. The goal is for users to enter a code (numbers) that will then be copied to the clipboard. By clicking on t ...

Transform basic text into nested JSON structure with JavaScript

There is a plain string in my possession which contains various conditions. const optionString = '{2109} AND ({2370} OR {1701} OR {2702}) AND {1234} AND ({2245} OR {2339})'; The goal is to transform this string into an object structured as foll ...

An easy way to incorporate a fade-in effect for every word in a text

I am trying to make the text "Eat. Sleep. Repeat." slide up and fade in one word at a time. I have experimented with various methods like anime.js, keyframes, and adding css classes, but so far none of them have worked for me. Here is the code I currently ...

Struggling to retrieve scope data within directive from controller in AngularJS

As a newcomer to AngularJS, I have utilized a service to retrieve data from the backend and received it in the controller. Now, my task is to parse these values and dynamically generate elements in a directive. However, when attempting to do so, I am encou ...

A guide on crafting OR queries in Elasticsearch using Node.js

I currently have a document structured like this: Sample document, [ { "_index": "xpertdox", "_type": "disease", "_id": "Ectopic Heartbeat", "_score": 24.650267, "_source": { "category": "Condition", "name": "Ectopic ...

Dealing with multiple jQuery ajax requests - strategies for managing them

Whenever I click the button quickly while there is jQuery Ajax loading, it seems to get stuck. How can I manage multiple requests being fired at the same time? What is the solution for the following: Cancel/abort all previous requests and only handle th ...

Is there a way to effectively transfer both Vertex and Face normals to a Three.js shader?

It seems that the THREE.Geometry methods, .computeFaceNormals() & .computeVertexNormals(), assign values to a built-in attribute array called "normal." If I want to utilize both vertex- & face-normals in one shader, I need to: Calculate face-normals ...

Can the serialization of AJAX URL parameters in jQuery be customized?

Is there a way to instruct jQuery or an AJAX call on how to format query string parameters other than writing a custom serializer? I am using a jQuery AJAX call and passing an object with URL parameters var params = {name: 'somename', favColors ...

Encountering an error while setting up the object spread operator Babel plugin for ES201

Exploring the possibilities of the new ES2018 spread operator for objects led me to discovering a promising NPM package: babel-plugin-transform-object-rest-spread Here's a glimpse of my package.json: // Scripts section "scripts": { "dev": " ...

The web application encountered an error: "Uncaught TypeError: Cannot read property 'map' of undefined" when trying to retrieve

Struggling with a simple API fetch, and despite checking everything multiple times, I can't seem to figure out what's going wrong. It feels like I'm missing something crucial. import { useState } from "react"; import ProductCard fr ...

Ways to extract text from a temporary element

Here is my HTML code: <div class="p-login-info" ng-show="loggedOut()">My text.</div> This is the corresponding JavaScript code: var e = element(by.className('p-login-info')); e.getText() .then(function(text){ var logoutText = ...

Conceal the div element if the value exceeds 0

I'm looking for a way to display a div when the number of remaining days is less than 0. I got it working on jsfiddle, but for some reason, it doesn't work anywhere else. if ($('.daysrem&a ...

The element vanishes from sight after being detached and re-appended

Currently, I am utilizing hQuery's draggable and droppable features. My goal is to move an element from its original parent to its new dropped parent after dragging it. However, when I detach and append the element, it seems to disappear! What could ...