Creating an interactive accordion table using vue.js and the v-for directive in the main element

Looking for some help on implementing this feature using VUE: https://jsfiddle.net/jacekpr/roschwvL/5/

Currently, my progress is here: https://jsfiddle.net/jacekpr/8bhpqc5s/13/

However, I'm facing an issue with having multiple root elements in the component while trying to render control-Component within risk-Component.

What I ideally want at the end of the risk-component is:

<control-Component v-for="control in risk.controls" :control="control" :key="risk.title + control.title" />

But I keep encountering this error message:

Component template should contain exactly one root element. If you are using v-if on multiple elements, use v-else-if to chain them instead.

Any suggestions on how to overcome this challenge?

Answer №1

To enhance the functionality of your table, you have the option to incorporate multiple tbody tags. Simply encapsulate risk-component within a tbody, allowing for several trs to be nested within it. It may require some restructuring to avoid nesting the tbodys.

var risks = '[{"title":"Risk1", "controls":[{"title":"Control1"}, {"title":"Control2"}, {"title":"Control3"}]},' +
  '{"title":"Risk2", "controls":[{"title":"Control1"}, {"title":"Control2"}, {"title":"Control3"}]},' +
  '{"title":"Risk3", "controls":[{"title":"Control1"}, {"title":"Control2"}, {"title":"Control3"}]}]';

var programs = '[{"title":"Program1", "practice":"IT", "auditType":"GAT", "version":"0.01", "programId":"2017.1", "status":"draft", "risks":' + risks + '},' +
  '{"title":"Program2", "practice":"IT", "auditType":"On-request", "version":"0.01", "programId":"2017.2", "status":"draft", "risks":""},' +
  '{"title":"Program3", "practice":"CA", "auditType":"GAT", "version":"0.01", "programId":"2017.3", "status":"approved", "risks":' + risks + '},' +
  '{"title":"Program4", "practice":"CA", "auditType":"On-request", "version":"0.01", "programId":"2018.1", "status":"draft", "risks":' + risks + '},' +
  '{"title":"Program5", "practice":"OA\FA", "auditType":"GAT", "version":"0.01", "programId":"2019.1", "status":"draft", "risks":' + risks + '},' +
  '{"title":"Program6", "practice":"OA\FA", "auditType":"On-request", "version":"0.01", "programId":"2020.1", "status":"approved", "risks":' + risks + '}]';


var dataO = JSON.parse(programs);

Vue.component('control-component', {
  props: ['control'],
  template: '<tr class="control" >' +
    '<td></td>' +
    '<td colspan="6"><a href="#">{{ control.title }}</a></td>' +
    '</tr>'
})

Vue.component('risk-component', {
  props: ['risk'],
  template: '<tbody><tr class="risk" >' +
    '<td></td>' +
    '<td colspan="5"><a href="#">{{risk.title}}</a></td>' +
    '<td><span class="signrisk"></span></td>' +
    '</tr>' +
    '<control-component v-for="control in risk.controls" :control="control" :key="control.title"></control-component>' +
    '</tbody>'
});


var programTable = new Vue({
  el: '#programTable',
  data: {
    programs: ''
  }
})
programTable.programs = dataO;
.signprogram:after {
  content: "+";
  display: inline-block;
  cursor: pointer;
}

.expandprogram:after {
  content: "-";
  cursor: pointer;
}

.signrisk:after {
  content: "+";
  display: inline-block;
  cursor: pointer;
}

.expandrisk:after {
  content: "-";
  cursor: pointer;
}

th {
  background-color: #e0e0e0;
}

.program {
  background-color: #e9e9e9;
}

.risk {
  background-color: #eeeeee;
}

.control {
  background-color: #f2f2f2;
}

.spacing {
  background-color: white;
}
<script src="//unpkg.com/vue@latest/dist/vue.js"></script>
<table class="table" width="300px" id="programTable">
  <thead>
    <tr>
      <th>Status</th>
      <th>Title</th>
      <th>Practice</th>
      <th>Audit Type</th>
      <th>Version</th>
      <th>Program</th>
      <th>&nbsp;</th>
    </tr>
  </thead>
  <template v-for="program in programs">
    <tbody>
      <tr class="program" >
        <td v-if="program.status == 'draft'" style="width: 20px; background-color: lightblue;">&nbsp;&nbsp;&nbsp;</td>
        <td v-if="program.status == 'approved'" style="width: 20px; background-color: lightgreen;">&nbsp;&nbsp;&nbsp;</td>
        <td><a href="/auditprograms/1">{{program.title}}</a></td>
        <td>{{program.practice}}</td>
        <td>{{program.auditType}}</td>
        <td>{{program.version}}</td>
        <td>{{program.programId}}</td>
        <td><span class="signprogram"></span></td>
      </tr>
    </tbody>
    <risk-component v-for="risk in program.risks" :risk="risk" :key="program.title + risk.title"></risk-component>
  </template>
</table>

Answer №2

When faced with a similar question, I propose an alternative solution: Vue js error: Component template should contain exactly one root element

In this scenario, you can assign the <tbody/> to a functional component that will generate a list of <tr/> components. Alternatively, delegate the <table/> to a functional component that will create a list of <tbody/> components. Or even utilize both methods.

Here is the code snippet:

If, for any reason, you prefer not to add a wrapper (as in the case of <tr/> components), a functional component can be used instead.

Instead of having a single components/MyCompo.vue, organize multiple files within a components/MyCompo folder:

  • components/MyCompo/index.js
  • components/MyCompo/File.vue
  • components/MyCompo/Avatar.vue

This structure allows seamless usage of the component as before.

Content of components/MyCompo/index.js file :

import File from './File';
import Avatar from './Avatar';   

const commonSort=(a,b)=>b-a;

export default {
  functional: true,
  name: 'MyCompo',
  props: [ 'someProp', 'plopProp' ],
  render(createElement, context) {
    return [
        createElement( File, { props: Object.assign({light: true, sort: commonSort},context.props) } ),
        createElement( Avatar, { props: Object.assign({light: false, sort: commonSort},context.props) } )
    ]; 
  }
};

If there are any shared functions or data between the templates, simply pass them as properties – it's that simple!

This approach opens up possibilities for creating lists of components and exploring various features.

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

Angular UI Bootstrap: Promise resolved or rejected upon modal closure

I'm currently working with angular and bootstrap, utilizing the angular-ui-bootstrap bridge library. My goal is to reuse the modal component and encapsulate it within a promise that will be fulfilled upon successful closure of the modal (by clicking t ...

Is there a way to prevent a page from automatically redirecting to another page without relying on user action or using the StopPropagation and window.onbeforeunload event?

function confirmExit(e) { var f = FormChanges(); //checking if the page has been modified if (f.length > 0){ if (submitForm == false) { if(!e) var e = window.event; e.cancelBubble = true; e.returnValue = "You ...

Tips for displaying a category name only once if it has already been rendered in a map function

My scenario involves a variety of categories in which pharmaceutical drugs are classified. Each individual drug belongs to a specific class. Currently, my code iterates through all the categories from the category array and places drugs underneath them if ...

Is there a way to post an array of MongoDB documents embedded in req.body?

How can I send the data from req.body to this MongoDB model using a POST request to '/add'? const QuestionSchema = new mongoose.Schema({ description: String, alternatives: [{ text: { type: String, ...

Making alterations to the content of a division using getElementById().innerHTML yields no difference

Recently, I've been working on a JS project inspired by a short story from my high school days. The story featured robot crabs living on a small island, and I wanted to create a simulator where players could set the initial parameters and watch as the ...

Generating npm package without including file extensions in imports

I am currently working on creating an internal library for my workplace. Everything seems to be going smoothly until I try to use it in another project. It appears that the file extension in all of the import statements has disappeared during the npm pack ...

Implement lazy loading functionality in Django to dynamically load data as the user scrolls

Currently, I am developing a web application using Django to showcase the interface and how content is loaded. In my database, there could potentially be thousands of entries, and I am looking for a way to load only a certain number at a time to minimize ...

What steps can I take to ensure that my header remains static while my slides move in fullpagejs?

I need help with fixing the header in my fullpagejs slide. The header currently moves along with the slide, but I want it to stay in place within the background image. When I try taking the header out of the background section, it creates a white space at ...

Subpar resolution of PNG images displayed in HTML canvas

My attempt to draw a PNG image onto the canvas is resulting in poor quality. I am using the drawImage method as shown below: src = folder+self.cur+".png"; imageObj.src = src; imageObj.onload = function() { context.clearRect(0, 0, cv, ch), context.drawImag ...

Utilizing the CSS 'overflow: hidden' property and jQuery to restrict users from scrolling during a loading page

OBJECTIVE I aim to restrict the user from scrolling while the page is loading. ISSUE The snippet of code provided successfully prevents the user from scrolling during the additional 2 seconds of the loader animation: $('body').toggleClass(&ap ...

I keep encountering the following issue: "It seems that the file requested at /images/crown.png is not recognized as a valid image, as it was received as text/html; charset=utf-8."

I encountered an issue while utilizing Next.js. Here is the code snippet where the error occurred: import React from "react"; import { Container, Col, Row } from "react-bootstrap"; import Image from "next/image"; export defaul ...

Component does not display dynamically created DOM elements

I have a function that creates dynamic DOM elements like this: const arrMarkup = []; const getMarkup = () => { if (true) { arrMarkup.push( <Accordion expanded={expanded === cust.name} onChange={handleChange(cust.name)}> ...

The error message "TypeError: event.preventDefault is not a function when using Formcarry with React Hook Form" is

How do I implement form validation using React Hook Form and Formcarry together? Something seems to be missing in my code or there might be a logic error Take a look at my code snippet: import { useForm } from "react-hook-form"; import { useFor ...

Unable to assign a scroll event delegation by using the "on" method

When attempting to delegate a scroll event in order for my element to maintain the same handler after being returned by an ajax call, I utilized the on method for delegation. $('body').on({ scroll:function(){ alert('scrolling&ap ...

how to retrieve the class name of a checkbox solely when it has been checked

I have some HTML code with two checkboxes, each with different class names: <p class="text"> <input type="checkbox" value="Yes" id="ques11" name="radiobutton" class="option_1" /> <label for="ques11">True&l ...

How can I open the Ion-datetime view for the current year without allowing the selection of a specific day?

I am currently troubleshooting an issue with an Ionic date-time picker component. Upon opening the datepicker, it defaults to showing May 2021. As I scroll to the present date, I notice a circle highlighting today's date indicating that it selects th ...

Identify all inputs that have been dynamically modified using AngularJS

I am currently working on a form that allows users to edit various fields. Some of these fields will automatically update with suggested values until the user makes changes ($dirty). To ensure users can see which fields have been modified, I would like to ...

Unable to load the requested resource: net::ERR_FAILED

I am facing an issue while trying to write React code. When using Chrome, I encountered the following warning: Access to XMLHttpRequest at 'file:///D:/programe/code/Vscode/ReactDemo/HelloWorld/test.js' from origin 'null' has been block ...

The CSS for a VueJS compiled app seems to fail to apply properly unless manually copied and pasted into the browser's style editor

After compiling my vuejs app with npm run build, I noticed that the CSS does not display when viewing it in Firefox. Surprisingly, the styles do load in the network tab and appear under the style editor, but with "0 rules". However, everything displays fin ...

Steps to deactivate the select element in backbone.js

Can someone help me with disabling the select option in my MVC template code using backbone.js? I tried using readonly="readonly" and disable="disable", but it sends null as value and doesn't update my address. <div class="login-register" data-val ...