Display nested array objects of varying levels within VUE

How do I display nested elements on the UI when dynamically generated based on a parent element's selected option (e.g. List)? For example, in the code below, when creating a field and selecting the List option, another nested should appear, and so on with an unknown depth. How can I render this for the user to see? Using v-for inside v-for doesn't seem to work. Do I need recursion here, but I'm unsure how to implement it.

Any help would be greatly appreciated!

var app = new Vue({
    el: '.container',
    data: {
        modelname: '',
                fields: []
    },
    methods: {

        addNewField() {
            this.fields.push({
            left: 0,
            type:'',
            method:'',
            size:'',
            value:''}
            )
        },
        createChildElement(field) {
        if (field.type == "List") {
        Vue.set(field, "value", []);
        field.value.push({
                   type:'',
                   left: field.left+20,
                   method:'',
                   size:'',
                   value:''}
                   );
 }
        
        },
        showJson() {
        const data = this.fields
    alert(JSON.stringify(data, null, 2));
        }
    }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div class="container">
            <h1 class="font-italic text-warning">TDGT Web Client</h1>
            <div>
                <button id="add-field" class="btn btn-sm btn-warning" @click="addNewField">Create field</button>
                <button id="show-json" class="btn btn-sm btn-warning" @click="showJson">Show JSON</button>
                <div v-for="(field, index) in fields" :style="{marginLeft: field.left+'px'}" :key="index">
                    <ul>
                        <li>
                    <select  v-model="field.type" v-on:change="createChildElement(field)"  aria-label="Choose field type">
                        <option selected>Field Type</option>
                        <option value="List">List</option>
                        <option value="Map">Map</option>
                        <option value="Integer">Integer</option>
                    </select>
                    <select  v-model="field.method" v-if="field.type === 'Map' || field.type === 'List'"  aria-label="Generation Method">
                        <option selected>Value Type</option>
                        <option value="Static">Static</option>
                        <option value="Random">Random</option>
                        <option value="Range">Range</option>
                    </select>
                    <input type="text" v-if="field.type === 'Map' || field.type === 'List'"  v-model="field.size" placeholder="Dimension">
                    <input type="text" v-if="field.type === 'Integer'"  v-model="field.value" placeholder="Value">
                        </li>
                        <ul></ul>
                    </ul>
                </div>

            </div>
    </div>

UPD. Following the suggestions provided, I attempted to find a solution for my task, but encountered some issues that I am unable to resolve. Some errors include:

Invalid prop: type check failed for prop "item". Expected Object, got Array.
Property or method "fields" is not defined on the instance but referenced during render.

Here is the updated code:

      Vue.component("el-inpt-group", {
        template: "#item-template",
        props: {
            item: Object,
          }
          });


var app = new Vue({
    el: '.container',
    data: {
        modelname: '',
        fields: [
        ]
    },
    methods: {

        addNewField() {
            this.fields.push({
            name: '',
            left: 0,
            type:'',
            method:'',
            size:'',
            value:''}
            )
        },
        createChildElement(field) {
        if (field.type == "List") {
        Vue.set(field, "value", []);
        field.value.push({
        name: '',
                   type:'',
                   left: field.left+20,
                   method:'',
                   size:'',
                   value:''}
                   )
        }
        },
        showJson() {
        const data = this.fields
    alert(JSON.stringify(data, null, 2));
        }
    }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>


    <script type="text/x-template" id="item-template">
            <ul>
                <li v-for="field in fields">
                    <input type="text"  v-model="field.name" placeholder="Name">
                    <select  v-model="field.type" v-on:change="createChildElement(field)"  aria-label="Choose field type">
                        <option selected>Field Type</option>
                        <option value="List">List</option>
                        <option value="Map">Map</option>
                        <option value="Integer">Integer</option>
                    </select>
                    <select  v-model="field.method" v-if="field.type === 'Map' || field.type === 'List'"  aria-label="Generation Method">
                        <option selected>Value Type</option>
                        <option value="Static">Static</option>
                        <option value="Random">Random</option>
                        <option value="Range">Range</option>
                    </select>
                    <input type="text" v-if="field.type === 'Map' || field.type === 'List'"  v-model="field.size" placeholder="Dimension">
                    <input type="text" v-if="field.type === 'Integer'"  v-model="field.value" placeholder="Value">
                </li>
                <ul><el-inpt-group v-for="child in fields.value" :style="{marginLeft: field.left+'px'}" :key="child.name" :item="child"></el-inpt-group></ul>
            </ul>
    </script>
</head>
<body>
<div class="container">
            <h1 class="font-italic text-warning">TDGT Web Client</h1>
           
                <button id="add-field" class="btn btn-sm btn-warning" @click="addNewField">Create Field</button>
                <button id="show-json" class="btn btn-sm btn-warning" @click="showJson">Show JSON</button>
                <el-inpt-group :item="fields"></el-inpt-group>

            </div>
    </div>

Answer №1

Recursion is the key to effectively organizing your code.

If you're struggling to structure your code, it likely means you need to break it down into more components.

For instance, you can create a component that accepts an item as a property and recursively calls itself if the item has sub-items.

Consider the following example structure:

[
  {
     name: 'Todo 1'
  },
  {
     name: 'Todo 2',
     items: [
       {
          name: 'Todo 2.1'
       },
       {
          name: 'Todo 2.2'
       }  
     ],
  },
  {
     name: 'Todo 3',
     items: []

  },
]
<template>
   <div>
      <article v-for="subTodos of todo.items" :key="subTodos.name"> 
          <h1>{{ subTodos.name }}</h1>
          <Todo :todo="subTodos" />
      </article>
   </div>
</template>

<script>
export default {
  name: 'Todo',
  props: {
    todo: { type: Object, required: true }
  }
}
</script>
<template>
   <Todo :todo="firstItem" />
</template>

<script>
export default {
   data () {
     return {
       firstItem: { name: 'First todo', items: nestedTodos }
     }
   }
}
</script>

In this example, each time todo.items contains elements, a new <Todo> component will be created, which in turn creates more <Todo> elements for any items they may have...

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

Mastering the Art of Page Scrolling with d3

I would like to implement a scrolling effect for my d3 that allows the entire page to scroll while panning, similar to the effect on challonge (http://challonge.com/tournaments/bracket_generator?ref=OQS06q7I5u). However, I only want the scrolling to occur ...

Is there a way in Vue.js to create a description list (dl) using v-for?

The v-for directive in vue is truly remarkable. I am currently faced with a situation where I need to create a description list similar to this. To achieve this, I have to generate two DOM elements for each item in my array: <dl class="row"> < ...

Using Struts2 to fetch data through AJAX and dynamically update a div element

Looking for advice on how to trigger an action class using JavaScript when a value is selected from a drop-down menu. The goal is to fetch data from a database and then update a table within a specific div without refreshing the entire page. The drop-down ...

variety of provider setups available in Angular

Trying to learn Angular from various online sources can be quite confusing, as different people use different patterns when writing functions. Can someone please explain the .provider concept in Angular? I have experimented with the .provider method using ...

What is the best way to eliminate the comma at the end of the final array

<% for(var i=0; i < 10; i++) { %> <a href="#"><%= related[i] %></a><% if(i !== 9) { %>, <% } %> <% } %> Displayed above is some code that includes a loop to display related items. The goal is to remove the comm ...

What is the definition of the term "WebapiError"?

I'm currently developing a Spotify Web App that focuses on retrieving the top albums of KD Rusha using the Client ID and Artist ID to exclusively fetch his releases on Spotify. To accomplish this, I am utilizing an npm package called spotify-web-api-n ...

Understanding Express.js API Function Parameters

The API documentation for Express mentions that the format for a function is "app.use([path,] function [, function...])". However, in the app.js file generated by the Express generator, there is a line of code like this: "app.use('/', routes);", ...

What is the process for dynamically updating a variable's state within a render function?

I'm currently working on a project for my class where we are tasked with creating a website using React. Unfortunately, neither myself nor my group members have been able to figure out how to render a function in a variable state and make it dynamic. ...

Updating a component with React JS setState() must be done on a mounted or mounting component

I encountered an issue where I am getting the error message setState(...): Can only update a mounted or mounting component., but I am struggling to find a solution. import React, { Component } from 'react'; import Loading1 from '../images/ ...

JavaScript's Circular Output

I am currently developing a calculator and I want it to round the results to approximately 5 decimal places. Below is the JavaScript code snippet I have written to take input and generate output: <div class="input"> Price paid per s ...

Leveraging React SSR with Next.js, we can utilize the `getInitialProps` method to insert a

When working on Next.js with server-side rendering in React, I encountered an issue while trying to render a page as shown below: // This common element is used in many projects through my private node_modules const Header = ({ result }) => <div> ...

Looking to showcase elements within an array inside an object using ng-repeat

In this JSON dataset, we have information about various anchoring products. These products include anchors, cleats, snubbers, shackles, and more. When it comes to displaying these products on an HTML page using Angular, make sure to properly reference the ...

You cannot use voice_channel.join() to create a music bot in discord.js v13

As I was working on a new music command feature for my Discord bot, I encountered an issue. Whenever I try to use the command -play {url}, I keep getting an error message that says: voice_channel.join is not a function. I searched through various resource ...

Guide to creating a React Hooks counter that relies on the functionality of both a start and stop button

I am looking to create a counter that starts incrementing when the start button is clicked and stops when the stop button is pressed. Additionally, I want the counter to reset to 1 when it reaches a certain value, for example 10. I have tried using setInte ...

Identifying Elements Generated on-the-fly in JavaScript

Currently, I am tackling the challenge of creating a box that can expand and collapse using regular JavaScript (without relying on jQuery). My main roadblock lies in figuring out how to effectively detect dynamically added elements or classes to elements a ...

Determining if a page was rendered exclusively on the client-side with NuxtJS and VueJS

As I develop an HTML5 video player component for a versatile Nuxt.js/Vue.js application, I encounter the challenge of enabling autoplay for videos upon navigation. Despite wanting to implement this feature, most browsers prohibit auto-playing videos direct ...

What is the best method to calculate the total of multiple input values from various cells and display it in the final cell of an Angular table?

Hey there! I have a challenge where I need to calculate the sum of input values for each cell and display it dynamically in the last cell of the row. Take a look at the image below: https://i.stack.imgur.com/0iKEE.png In the image, you can see that the nu ...

The use of a <button> element in a React App within a Web Component with Shadow DOM in Chrome disables the ability to highlight text

An unusual problem has arisen, but I have a concise example that demonstrates the issue: https://codesandbox.io/s/falling-architecture-hvrsd?file=/src/index.js https://i.stack.imgur.com/CkL4g.png https://i.stack.imgur.com/nDjuD.png By utilizing the divs ...

The jQuery click event is failing on the second attempt

My PHP code dynamically generates a list, and I want to be able to delete rows by clicking on them. The code works fine the first time, but not the second time. HTML <li> <div class="pers-left-container"> <img src="<?php ech ...

Encountering an issue with my node configuration while working on a Discord bot

Attempting to develop my own Discord Bot has presented me with a challenging error that I am struggling to resolve: internal/modules/cjs/loader.js:968 throw err; ^ Error: Cannot find module './commands/${file}' Require stack: - C:\Users&bso ...