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

Having trouble with jQuery events not triggering properly after dynamically inserting elements using an ajax request?

It's strange that all my jQuery events become unresponsive after an AJAX call. When I use a load function, once the JSP reloads, none of the events seem to work properly. Any suggestions? Below is the code that triggers the function call: $('#p ...

Exploring the methods for retrieving and setting values with app.set() and app.get()

As I am granting access to pages using tools like connect-roles and loopback, a question arises regarding how I can retrieve the customer's role, read the session, and manage routes through connect-roles. For instance, when a client logs in, I retrie ...

Running a function with a parameter upon clicking results in an error

I am facing an issue with a computed Vue function that includes a parameter. Whenever I attempt to bind it to a click event, I encounter the following error message: Error in event handler for "click": "TypeError: searchHashtag is not a function" Below is ...

Change the name of "rows" in the findAndCountAll method of Sequelize

Is there a way to change the key name for result.row when utilizing findAndCountAll in Sequelize? For pagination purposes, I am implementing findAndCountAll. The data structure I receive is as follows: { "count": 8, "rows": [ { ...

The Correct Way to Implement Return in jQuery Ajax

Encountering an issue with the login form where I am unable to proceed to the next page after entering my credentials. The system acknowledges if the email and password match, however, the login does not go through. Seeking assistance in identifying the ...

JavaScript Grouping Arrays

I am working with an array and need to filter it by both Country and Service. So far, I have successfully filtered the array by Country. Now, I want to achieve the same filtering process based on the Service as well. Here is a snippet of the array: [ ...

Vue Js error message: The following dependency could not be found: * core-js/fn/promise

While working on my Vuejs App, I encountered an error related to promises: I received the following error message: "This dependency was not found: core-js/fn/promise in ./src/store/index.js. To resolve this, you can run: npm install --save core-js/fn/promi ...

I'm encountering an issue with my React master component not being able to locate the

I am having trouble importing a component in my APP.js file. I have been attempting to bring MainComponent into the app.js component, but I am facing difficulties in fetching the component. Any assistance in resolving this issue would be greatly apprecia ...

Tips on incorporating personalized javascript functions into Durandal

I am currently working on my first project using the Durandal framework to create a basic website. I have encountered an issue while trying to incorporate a simple JavaScript function. My goal is to execute the function after the DOM has loaded, but I am u ...

Updating Rails record using Ajax with select_tag and onchange functionality

I'm working on an index view where I want to update a table data dynamically using select_tag onchange feature without the need to refresh the page. Do I need to use Coffeescript to detect the onchange event, capture the new value, and then send it to ...

What could be the reason for the "category empty" message appearing at the first index after clicking on the add

When I click on the add products button, why is the category name empty at the first index? 1. The text starts from index 2 of the category column. 2. When I change the value from the dropdown, it first displays the previous value in the category column an ...

How can I ensure my game or website fits perfectly on the screen without any need

I have recently developed a fun Erase-A-Man game that is optimized for mobile devices. However, I am facing an issue where users need to scroll to view all the letters on their screens. My goal is to make the game fit perfectly on all phone screen sizes so ...

What is the best way to deactivate buttons with AngularJS?

I have a situation where I need to disable the save button in one function and permanently disable both the save as draft and save buttons in another function using AngularJS. How can I accomplish this task with the disable functionality in AngularJS? Her ...

Managing Ajax error messages

$.getJSON("data.php", function(data) { ... this callback is for handling successful retrieval of data }); What is the best way to manage errors in relation to the ongoing $.getJSON request? ...

When attempting to pass Rgraph image data through a jQuery AJAX call, a 403 Forbidden error is being

I have been working on a project that involves creating graphs/charts using the Rgraph PHP library. To generate these charts, my script follows these steps: Calculate the graph points and render the graph using the Rgraph Draw() method. Create an image d ...

"Troubleshooting: Why is my Bootstrap modal window only printing a

I've encountered an issue with printing the content of a Bootstrap modal window. Previously, the code I had was working fine but now it seems to be malfunctioning. Content is dynamically added to the form using appendChild() in a separate function. Ho ...

Issue with Masonry.js implementation causing layout to not display correctly

Currently, I am working on a project using Laravel, VueJS, and the Masonry.js library to develop a dynamic gallery. However, I have encountered a peculiar issue. Here is a snippet of my VueJS template: <template lang="html"> <div id="uploads-g ...

Creating dynamic templates and embellishments in VUE

My VUE components, including Field and Form, are able to dynamically render a form based on the provided data. <template> <form @submit="$emit('submit', $event)" > <template v-for="(item, index) in form.elemen ...

Localizing strings that are not saved in a database

Our web app will soon support multiple languages, a new feature we are excited to roll out! Currently, we utilize Handlebars for front-end templating and Node + Jade for back-end templating. As we prepare to implement language support, we're conside ...

"Execute asynchronous tasks in Javascript and receive the returned

Currently, I am utilizing JSF ajax within my application and unfortunately, we are unable to make any changes to that. In the process of waiting for user action, I find it necessary to prompt the user before executing the ajax method. Specifically, I need ...