Switching individual items created by a v-for loop in Nuxt.js

I am struggling to create a simple accordion-like structure with the ability to toggle individual elements:

<div v-for="qa, j in group.questions_answers" :key="j">
  <div class="question" @click="toggle()" > <!-- use index here? -->
    <span v-if="itemOpen" class="font-iconmoon icon-accordion-up"><span/>
    <span v-else class="font-iconmoon icon-accordion-down"><span/>
    <span class="q-text" v-html="qa.question"><span/>
  </div>
  <div v-if="itemOpen" class="answer" v-html="qa.answer"></div>
</div>

How can I toggle individual question/answer blocks without affecting all elements? Should I use refs for this functionality?

At present, I am able to toggle all elements at once...

export default {
  data() {
    return {
      itemOpen: true
    }
  },
  methods: {
    toggle() { // index
      this.itemOpen = !this.itemOpen
    }
  }
}

Creating a separate Q/A component with internal toggling logic seems like an excessive solution for this issue.

UPDATE: My data structure is as follows:

{
    "intro_text": "intro text",
    "questions_groups": [
        {
            "group_heading": "heading",
            "questions_answers": [
                {
                    "question": Question",
                    ""answer": "Answer"
                },
                {
                    ""question": "Question",
                    ""answer": "Answer"
                },
                {
                    "question": "Question",
                    "answer": "Answer"
                },
                {
                    ""question": "Question",
                    ""answer": "Answer"
                },
                {...}
            ]
        },
        {
            ""group_heading": "heading 1",
            ""questions_answers": [
                {
                    ""question": "Question",
                    ""answer": "Answer"
                },
                {
                    ""question": "Question",
                    ""answer": "Answer"
                },
                {
                    ""question": "Question",
                    ""answer": "Answer"
                },
                {
                    ""question": "Question",
                    ""answer": "Answer"
                },
                {...}
        },
        {
            ""group_heading": "heading 2",
            ""questions_answers": [
                {
                   "question": "Question",
                    ""answer": "Answer"
                },
                {
                   ""question": "Question",
                    ""answer": "Answer"
                },
                {
                   ""question": "Question",
                    ""answer": "Answer"
                },
                {
                    ""question": "Question",
                    ""answer": "Answer"
                }
                {...}
            ]
        }
    ]
}

Answer №1

If you want to mutate a property of an object into an array for looping, follow this example.

<template>
  <!-- eslint-disable vue/no-v-html -->
  <article>
    <div
      v-for="todo in todoList"
      :key="todo.id"
      class="question"
      @click="toggleMyTodo(todo)"
    >
      <span
        v-if="isTodoDone(todo)"
        class="font-iconmoon icon-accordion-up"
      ></span>
      <span v-else class="font-iconmoon icon-accordion-down"></span>
      <span class="q-text" v-html="todo.question"></span>
      <div v-if="isTodoDone(todo)" class="answer" v-html="todo.answer"></div>
    </div>
  </article>
</template>

<script>
export default {
  data() {
    return {
      todoList: [
        {
          id: 1,
          task: 'Cook',
          done: false,
          question: 'duh?',
          answer: 'ahh okay!',
        },
        {
          id: 2,
          task: 'Hover',
          done: false,
          question: 'duh?',
          answer: 'ahh okay!',
        },
        {
          id: 3,
          task: 'Washing machine',
          done: false,
          question: 'duh?',
          answer: 'ahh okay!',
        },
      ],
    }
  },
  methods: {
    toggleMyTodo({ id }) {
      const currentTodoIndexToToggle = this.todoList.findIndex(
        (todo) => todo.id === id
      )
      // $set is used because of this https://v2.vuejs.org/v2/guide/reactivity.html#For-Arrays
      this.$set(this.todoList, currentTodoIndexToToggle, {
        ...this.todoList[currentTodoIndexToToggle],
        done: !this.todoList[currentTodoIndexToToggle].done,
      })
    },
    isTodoDone({ id }) {
      // I'm using `?.` just to be sure https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining
      return this.todoList.find((todo) => todo.id === id)?.done
    },
  },
}
</script>

By the way, it is recommended not to use v-html directly but pass it through a sanitizer.


Just a reminder that we're utilizing this.$set due to Vue's specific requirements, more information can be found here.

Answer №2

Due to the absence of an id and done field on the individual question/answer object, as well as the requirement to loop through multiple q/a groups, it proved more efficient to create a sub component in this scenario for targeting the specific q/a rows for toggle functionality:

<template>
  <div>
    <slot-main-heading>{{$t('faq.h1')}}</slot-main-heading>
    <div v-html="introText"></div>
    <div v-for="(group, i) in questionsGroups" :key="i">
      <slot-htwo-heading>{{group.group_heading}}</slot-htwo-heading>
      <template v-for="(qa, j) in group.questions_answers">
        <cms-accordion-row :qaData="qa" :key="j"/>
      </template>
    </div>
  </div>
</template>

cmsAccordionRow.vue

<template>
<div>
  <div class="question" @click="toggle()">
      <span v-if="itemOpen" class="font-iconmoon icon-accordion-up" ></span>
      <span v-else class="font-iconmoon icon-accordion-down" ></span>
      <span class="q-text" v-html="qaData.question" ></span>
  </div>
  <div v-if="itemOpen" class="answer" v-html="qaData.answer"></div>
</div>
</template>

<script>
export default {
  props: {
    qaData: {
      type: Object,
      required: true
    }
  },
  data() {
    return {
      itemOpen: true
    }
  },
  methods: {
    toggle() {
      this.itemOpen = !this.itemOpen
    }
  }
}
</script>

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

Utilize a method in Vue.js to filter an array within a computed property

I have a question regarding my computed property setup. I want to filter the list of courses displayed when a user clicks a button that triggers the courseFilters() method, showing only non-archived courses. Below is my current computed property implement ...

Refresh the page with user input after a button is clicked without reloading the entire page, using Python Flask

My python/flask web page accepts user input and returns it back to the user without reloading the page. Instead of using a POST request, I have implemented Ajax/JavaScript to handle user input, process it through flask in python, and display the result to ...

Creating a Vue.js application using Visual Studio Code

Recently delving into the world of vue.js, I've turned to Visual Studio Code for my coding needs. Following the instructions provided in the Volar extension guide, I can see that the sample project includes a 'hello world' page (HelloWorld.v ...

How can I use a button created with jQuery's .html() method to conceal a <div>?

I am facing an issue with implementing a simple banner that should appear in an empty element only when specific values are returned by an Ajax call. In the banner, I want to include a Bootstrap button that hides the entire div when clicked. Due to my la ...

JavaScript Language Conversion Templating

I'm currently revamping the frontend for Facebook's internationalization XFBML tag, which has been nonfunctional for a while. I'm almost done with the updates but I have hit a roadblock: swapping out tokenized translations without losing dat ...

Make sure to include the onBlur and sx props when passing them through the slotsProp of the MUI DatePicker

This DatePicker component is using MUI DatePicker v6. /* eslint-disable no-unused-vars */ // @ts-nocheck import * as React from 'react'; import { Controller } from 'react-hook-form'; import TextField from '@mui/material/TextField&a ...

Unable to execute JavaScript on Node.js

I recently installed Node.js so I could run some JavaScript codes on LeetCode, but unfortunately, I encountered an issue. I then tried installing the Code Runner extension on VS Code to see if that would help, but this is the output I received: [Running] n ...

Oops! Looks like there's an unexpected error with the module 'AppRoutingModule' that was declared in the 'AppModule'. Make sure to add a @Pipe/@Directive/@Component annotation

I am trying to create a ticket, but I encountered an error. I am currently stuck in this situation and receiving the following error message: Uncaught Error: Unexpected module 'AppRoutingModule' declared by the module 'AppModule'. Plea ...

Ensuring the successful execution of all AJAX calls (not just completion)

I've seen this question asked many times about how to trigger a function once all AJAX calls have finished. The typical solution involves using jquery.stop(). However, my situation is unique - I want to display a confirmation banner only after all AJA ...

Guide on converting this function into a computed property within Vue

Is it possible to concatenate a fixed directory path, which is defined in the data property, with a file name that is determined using v-for? I encountered an issue when attempting to do this using a computed property. The error message displayed was: ...

I'm trying to access my navigation bar, but for some reason, it's not allowing me to do so

Having trouble getting the navigation bar to open. I have set it up so that when clicked, it should remove the hide-links tag, but for some reason, it does not toggle properly and keeps the ul hidden. import React from "react"; import { NavLink } ...

If the href attribute is set to "#" or the target attribute is set to "_blank", then the <a> request cannot be prevented

I have a registration site that triggers a warning window whenever a user clicks on any link during the registration process. However, I want to exclude <a> tags with href="#" and target="_blank" attributes from this behavior. Essentially, I want to ...

Angular ng-repeat encounters difficulty in parsing Arabic object

As I create a JSON object that contains Arabic content shown below $scope.arabicContent = ["ردهة","قاعة الاجتماعات","مبرمجين الجوال","المدراء","المحاسبة","المحاسبة","المبرمجين‎","مطبخ‎ ...

Concealing the nearest object

I am currently working on using jquery to hide certain content on a website's index page. Within the fiddle, there is commented out code which I have been experimenting with - however, it hides all content divs if any toggle link is clicked. HTML & ...

Step-by-step guide on incorporating an external JavaScript library into an Ionic 3 TypeScript project

As part of a project, I am tasked with creating a custom thermostat app. While I initially wanted to use Ionic for this task, I encountered some difficulty in integrating the provided API into my project. The API.js file contains all the necessary function ...

"Despite not preferring to use the CORS wildcard, I am encountering a CORS wildcard error

I'm currently in the process of developing a web application using node, express, and Vue3 My goal is to submit a form to an endpoint located on my router at '/match/matchpost'. I have previously tested this endpoint successfully. However, ...

Steps to show an input button and exit the current window

I am looking for guidance on how to enable or display an input on a webpage if an ajax call is successful. I want this input, when clicked, to be able to close the current window using JavaScript. What would be the most efficient way to accomplish this? B ...

Tips for fixing Unexpected Token Error in a nextJS project

I recently started working on a brand new "next.js" project and was eager to dive into coding. However, I found myself stuck for hours trying to resolve the following error message. To kick things off, I decided to use a reference code snippet which looke ...

What is the process for incorporating an additional input in HTML as you write?

I am looking to create a form with 4 input boxes similar to the layout below: <input type="text" name="txtName" value="Text 1" id="txt" /> <input type="text" name="txtName2" value="Text 2" id="txt" /> <input type="text" name="txtName3" valu ...

Is there a way to dismiss a modal window from the parent?

How can I close a modal window from its parent? This task seems quite challenging to accomplish. Essentially, I am opening a non-modal window. In some cases, the user may open a modal window from this non-modal window. If I close the non-modal window, I al ...