Exploring the capabilities of Vue through Render Functions and Recursive Components

I'm struggling to fully understand recursive components, but I believe they are the best approach for what I'm trying to achieve. Here is a Fiddle showing my progress so far:

Check out the Fiddle here

My goal is to traverse through a nested JSON structure that simulates a DOM. Each "node" has properties like this:

"tagName": "section",
"classes": ["container", "mx-auto"],
"attrs": {"id":"main"},
"textNode": "",
"children": [{}]

Currently, I can recursively create each node as a component and store them in an array within the Vue Instance. However, I'm facing an issue where the child components need to be displayed inside their parent component. I'm considering creating an object with component objects and using a recursive component to parse them, but I'm unsure how to proceed.

Another idea is to create a flat array of components with parent IDs, and then somehow utilize this data structure?

I would appreciate guidance on how to tackle this challenge. I believe a recursive component will be beneficial, but I'm not sure how to integrate it with Create Element/Render Functions. Each node should have two-way binding with class lists, attribute lists, etc., which I plan to manage using states/stores or possibly Vuex.

The current code in the Fiddle displays all components from the JSON structure one after another without nesting.

const jsonData = [
    {
        "tagName": "section",
        "classes": ["container","mx-auto"],
        "attrs": {},
        "textNode": "",
        "children": [
            {
                "tagName": "div",
                "classes": ["flex","flex-wrap"],
                "attrs": {},
                "textNode": "",
                "children": [
                    {
                        "tagName": "div",
                        "classes": ["w-1/2"],
                        "attrs": {},
                        "textNode": "Hello"
                    },
                    {
                        "tagName": "div",
                        "classes": ["w-1/2"],
                        "attrs": {},
                        "textNode": "Goodbye"
                    }
                ]
            }
        ]
    }
];

let Components = [];
let uuid = 0;

function recurse() { 
    recursiveInitialize(jsonData)
}

function recursiveInitialize(j) {

    
    if (Array.isArray(j)) {
        return j.map((child) => recursiveInitialize(child))
    }

    if (j.children && j.children.length > 0) {


         initializeComponent(j)

        console.log("Hi I am " + j["tagName"] + " and a parent")

        j.children.forEach((c) => {
            console.log("Hi I am " + c["tagName"] + " and my parent is " + j["tagName"])
            recursiveInitialize(c)
        });


    }

    else {
        console.log("Hi, I dont have any kids, I am " + j["tagName"])
        initializeComponent(j)
    }
}
    

function initializeComponent(jsonBlock){
    let tempComponent = {
        name: jsonBlock["tagName"]+ uuid.toString(),
        methods: {
            greet() {
                store.setMessageAction(this)
            }
        },
        data: function() {
            return {
                tagName: jsonBlock["tagName"],
                classes: jsonBlock["classes"],
                attrs: jsonBlock["attrs"],
                children: jsonBlock["children"],
                textNode: jsonBlock["textNode"],
                on: {click: this.greet},
                ref: uuid,
            }
        },
        beforeCreate() {
            this.uuid = uuid.toString();
            uuid += 1; 
        
        
        },
        render: function(createElement) {
              return createElement(this.tagName, {
                class: this.classes,
                on: {
                    click: this.greet
                },
                attrs: this.attrs,
            }, this.textNode);
        },
        mounted() {
            // example usage
            console.log('This ID:', this.uuid);
        },
    }
    Components.push(tempComponent);
    return tempComponent
}

const App = new Vue({
    el: '#app',

    data: {
        children: [
            Components
        ],
    },
    beforeCreate() {
        recurse();
        console.log("recurseRan")
    },

    mounted() {
        this.populate()
    },

    methods: {
        populate() {
            let i = 0;
            let numberOfItems = Components.length;

            for (i = 0; i < numberOfItems; i++) {
                console.log("populate: " + Components[i])
                this.children.push(Components[i]);
            }

        },
    }
});
 
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>


<div id="app">
    <template v-for="(child, index) in children">
        <component :is="child" :key="child.name"></component>
    </template>
</div> 

Answer №1

Have you attempted to implement something similar to this?

// MyComponentRecursive.vue
<template>
  <div>
    <!-- content of the node -->
    <div v-for="childNode" in jsonData.children">
      <MyComponentRecursive :jsonData="childNode" />
    </div>
  </div>
<template

Answer №2

    const jsonData = [
        {
            "tagName": "section",
            "classes": ["container","mx-auto"],
            "attrs": {},
            "textNode": "",
            "children": [
                {
                   "tagName": "div",
                    "classes": ["flex","flex-wrap"],
                    "attrs": {},
                    "textNode": "",
                    "children": [
                        {
                            "tagName": "div",
                            "classes": ["w-1/2"],
                            "attrs": {},
                            "textNode": "Hello"
                        },
                        {
                            "tagName": "div",
                            "classes": ["w-1/2"],
                            "attrs": {},
                            "textNode": "Goodbye"
                        }
                    ]
                }
            ]
        }
    ];
  


  let ComponentsData = [];
  let uniqueId = 0;
   

  function performRecursion() { 
          initializeRecursiveProcess(jsonData)
    
  }

   function initializeRecursiveProcess(jsonObject) {

      
      if (Array.isArray(jsonObject)) {
          return jsonObject.map((child) => initializeRecursiveProcess(child))
      }

      if (jsonObject.children && jsonObject.children.length > 0) {


           initiateComponentCreation(jsonObject)

          console.log("Hi I am " + jsonObject["tagName"] + " and a parent")


          jsonObject.children.forEach((childElement) => {
              console.log("Hi I am " + childElement["tagName"] + " and my parent is " + jsonObject["tagName"])
              initializeRecursiveProcess(childElement)
          });


      }

      else {
          console.log("Hi, I dont have any kids, I am " + jsonObject["tagName"])
          initiateComponentCreation(jsonObject)
      }
  }
  

function initiateComponentCreation(jsonEntity){
      let temporaryComponent = {
      name: jsonEntity["tagName"]+ uniqueId.toString(),
          methods: {
              greet() {
                  store.setMessageAction(this)
              }
            },
          data: function() {
              return {
                  tagName: jsonEntity["tagName"],
                  classes: jsonEntity["classes"],
                  attrs: jsonEntity["attrs"],
                  children: jsonEntity["children"],
                  textNode: jsonEntity["textNode"],
                  on: {click: this.greet},
                  ref: uniqueId,
              }
          },
          beforeCreate() {
              this.uuid = uniqueId.toString();
              uniqueId += 1; 
              
          
          },
          render: function(createElement) {
                return createElement(this.tagName, {
                  class: this.classes,
                  on: {
                      click: this.greet
                  },
                  attrs: this.attrs,
              }, this.textNode);
          },
          mounted() {
              // example usage
              console.log('This ID:', this.uuid);
          },
      }
      ComponentsData.push(temporaryComponent);
      return temporaryComponent
  }
   
  const AppInstance = new Vue({
      el: '#app',

      data: {
          children: [
              ComponentsData
          ],
      },
      beforeCreate() {
          performRecursion();
          console.log("recurseRan")
      },

      mounted() {
          this.populate()
      },

      methods: {
          populate() {
              let indexValue = 0;
              let totalItems = ComponentsData.length;

              for (indexValue = 0; indexValue < totalItems; indexValue++) {
                  console.log("populate: " + ComponentsData[indexValue])
                  this.children.push(ComponentsData[indexValue]);
              }

          },
      }
  });
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>


                      <div id="app">
                          <template v-for="(child, index) in children">
                              <component :is="child" :key="child.name"></component>
                          </template>
                      </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

Tips on transferring information from React frontend Hooks to the Express backend

I'm encountering an issue when attempting to send data from my React front end, which is utilizing hooks, to my Node/Express server. Upon inspecting the request object (userLog) in the Express back end, I notice that the body content is empty. Can an ...

What is the reason this :class="{}" is not functioning properly in Vue.js?

I have created a simple file that contains a reactive Array of objects where each object has a property called "checked" which is a boolean that toggles based on a checkbox. I am using a v-for directive to iterate through the array of employees and render ...

Trigger a click event using jQuery

When the page loads, I need to call a function. There are various methods that can achieve this. Adding $('#button').click before my function causes an issue where the getType function is not recognized. Here's an example: $('#button&a ...

Having trouble getting tagmanager.js to work on my JSF 2.2 form. Can anyone help me troubleshoot what is going wrong?

Struggling for hours to make tagmanager.js function on my JSF 2.2 page has left me perplexed. I was successful using jsp pages, but they are outdated now. Even though I can input the tags properly, they fail to reach my java bean upon form submission. Titl ...

`What's the best way to merge 3 IDs after pressing a button?`

I attempted this code <p>Watch Series Online</p> <input type="search" id="imdbseries" class="Search" name="" value="" placeholder="IMDB ID"> <input type="search" i ...

Is there a keen eye out there that can help me pinpoint the mistake in this SVG/Javascript function?

I have created a series of SVG circles that are meant to alternate in color between blue and red with each click. However, I am experiencing some inconsistency in the behavior. In my local environment, the color doesn't change to red until the second ...

Durable Container for input and select fields

I need a solution for creating persistent placeholders in input and select boxes. For instance, <input type="text" placeholder="Enter First Name:" /> When the user focuses on the input box and enters their name, let's say "John", I want the pl ...

Clicking on the menu in mobile view will cause it to slide upward

I have implemented sticky.js on my website and it is working well. However, when I resize the browser to mobile view and click the main menu button, it goes up and I am unable to close it. I have to scroll up to see it again. How can I make it stick to the ...

Add every WebSocket message to a div element in VUE3

Currently, I am facing an issue while attempting to display each trade from Binances Websocket Stream in my VUE3 component. The challenge I'm encountering is that only one line gets rendered and it keeps updating continuously. This is not the desired ...

What is the counterpart of Python's pip freeze in Node.js?

Is there a way to generate a list of all npm modules being used along with their versions similar to Python's pip freeze? Also, is there a command in Node.js equivalent to Python's pip install -r /path/to/requirements.txt for reproducing the envi ...

Using Raycaster in ThreeJS to pick out faces from models loaded with STL Loader

I am currently developing a .stl viewer using Three.js with the objective of selecting and calculating specific areas within the model. To achieve this area calculation, I need the capability to select faces (e.g. change color). Although I came across som ...

I'm having trouble getting the code to work properly after the "else" statement using both jQuery and Javascript. My head is

Being a newcomer to javascript and jquery, debugging and viewing log files seem like a challenge compared to php. Any help from experienced individuals would be greatly appreciated. Although my code mostly works fine, I'm having trouble with the if/e ...

Unable to display an image fetched from Django API within VueJs

Can anyone help with rendering an image from Django RestAPI to Vuejs Frontend? I am able to retrieve all model elements in VueJS, but encountering issues when it comes to rendering images. invalid expression: Unexpected token { in ${article.image} ...

Vue component not displaying user information after successful login

After a successful login, I am utilizing localStorage to store user information. I am attempting to use a v-if statement to determine if the user is currently logged in or not. However, I am encountering an issue where localStorage is returning null when I ...

React and Redux are throwing an error stating that actions must be simple objects. For any asynchronous actions, it is recommended to utilize

Below is the code for my Component UserHandler import React, { Component } from 'react'; import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import * as actionCreators from '../../store/actions/ac ...

CodeIgniter functionality for generating auto-incrementing IDs that are accessible in both the view and within JavaScript's Window.Print() method

While working on creating an invoice, I encountered a few issues. First, I want the Invoice No: to be displayed in my view (receipt.php) as 0001 and use it as the primary key in my tbl_payment table. However, I'm unsure how to have an auto-incremented ...

What is the reason behind vue-datepicker automatically adding 4 hours to the user-selected time input?

I'm currently facing an issue with integrating vue-datepicker within Laravel Breeze/Vue. For some reason, the datepicker seems to add 4 hours to the time selected by the user. This means that if a user picks 10:00 AM, the datepicker displays it as 14: ...

Storing and Retrieving Cookies for User Authentication in a Flutter Application

I am currently working on developing a platform where, upon logging in, a token is created and stored in the cookie. While I have successfully implemented a route that stores the cookie using Node.js (verified in Postman), I encounter issues when attemptin ...

Double-tap bug with Image Picker in Typescript React Native (non-expo)

Well, here’s the situation - some good news and some bad news. First, the good news is that the code below is functioning smoothly. Now, the not-so-good news is that I find myself having to select the image twice before it actually shows up on the clie ...

Visuals derived from JSON information

Here is the JSON data retrieved from a web API: [{"FileName":"D:\\StuckUpTask\\Insta\\Insta\\Images\\/download (1).jpg"},{"FileName":"D:\\StuckUpTask\\Insta\\Insta\&bsol ...