Enhance the original array of a recursive treeview in VueJS

After going through this tutorial, I decided to create my own tree view using recursive components in Vue.js. The structure of the input array is as follows:

let tree = {
  label: 'root',
  nodes: [
    {
      label: 'item1',
      nodes: [
        {
          label: 'item1.1'
        },
        {
          label: 'item1.2',
          nodes: [
            {
              label: 'item1.2.1'
            }
          ]
        }
      ]
    }, 
    {
      label: 'item2'  
    }
  ]
}
<template>
  <div>
    ...
    <tree-menu 
      v-for="node in nodes" 
      :nodes="node.nodes" 
      :label="node.label" />
    ...
  </div>
<template

<script>
  export default { 
    props: [ 'label', 'nodes' ],
    name: 'tree-menu'
  }
</script>

Essentially, a label and an array of nodes are passed down to child nodes. Now, my goal is to make updates or deletions to a node (e.g. item1.1) and have these changes reflected in the main array (in this case tree) so that I can send this updated structure to the server. How can I achieve this? When I modify the label of a node, it updates in the DOM but does not update the tree array.

Answer №1

Learn how to implement the .sync modifier for recursive updates:

Vue.config.devtools = false;
Vue.config.productionTip = false;
Vue.component('tree-node', {
  template: `
<div style="margin-left: 5px;">
  <input :value="label"
         type="text"
         @input="$emit('update:label', $event.target.value)" />
  <tree-node v-for="(node, key) in nodes"
             :key="key"
             v-bind.sync="node" />
</div>
`,
  props: ['label', 'nodes']
});

let tree = {
      label: 'root',
      nodes: [{
          label: 'item 1',
          nodes: [
            { label: 'item 1.1' },
            { label: 'item 1.2', 
              nodes: [
                { label: 'item 1.2.1' }
              ]
            }
          ]
        },
        { label: 'item 2' }
      ]
    };

new Vue({
  el: '#app',
  data: () => ({
    tree
  })
})
#app {
  display: flex;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.js"></script>
<div id="app">
  <div>
    <tree-node v-bind.sync="tree" />
  </div>
  <pre v-html="tree" />
</div>

v-bind.sync="node" is a shortcut for

:label.sync="node.label" :nodes.sync="node.nodes"
. This syntax allows all object members to be passed as attributes of the tag, serving as props for the component.

The approach also includes replacing v-model on the input with :value and using

$emit('update:propName', $event.target.value)
on @input to update the property specified by .sync in the parent component. Essentially, it offers a customizable alternative to the standard v-model, allowing control over when and what to update. Different types of inputs can be used based on specific requirements (checkboxes, textarea, select, etc.). Customized listeners like @change or @someCustomEvent can be applied accordingly.

By utilizing .sync, reactivity is maintained at each level without triggering unnecessary re-renders (Vue only updates DOM elements that have actually changed). This ensures smooth user interactions, such as not losing focus in input fields during updates.

The concept mirrors the principles of Vuex where mutations are handled centrally through store actions impacting the local state across multiple components rather than isolated changes within one component.

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

Move the cache folder for NextJS to a new location

Is it possible to customize the location of the cache folder currently located in "./.next/cache"? I am interested in modifying this because I am developing an application that receives high traffic daily, and I plan to deploy multiple applications from m ...

Implementing user profile picture display in express.js: A step-by-step guide

I'm having trouble displaying the profile picture uploaded by users or employees in my application. Although the picture uploads successfully, it doesn't show up and gives an error when I try to redirect to the page. Cannot read property &ap ...

A Sweet Alert to Deliver your Morning Toasty Message

I seem to be encountering an issue with my toast message. Instead of the toast popping up immediately after submitting the form, it keeps appearing even if I haven't submitted the form and even when navigating from another page to this one, the toast ...

Leveraging the Google Feed API using jQuery's AJAX functionality

Currently, I am attempting to utilize Google's Feed API in order to load an RSS feed that returns a JSON string. (For more information, please refer to: https://developers.google.com/feed/). Despite this, my approach involves using jQuery's AJ ...

Convert Binary Data to PDF Using Javascript Through Streaming

Upon requesting a Web-service, I received the following response (PDF file Streamed) %PDF-1.5 %µµµµ 1 0 obj <</Type/Catalog/Pages 2 0 R/Lang(en-GB) /StructTreeRoot 10 0 R/MarkInfo<</Marked true>>>> endobj 2 0 obj <</Type/ ...

Generate a fresh FileReader instance using the downloaded file via XmlHTTPRequest

I am attempting to use an XmlHTTPRequest object (level 2) downloaded through a "GET" request in order to create a new FileReader object. My goal is to create the FileReader object within the onload function of the xhr. The file, which is a .gz file, downl ...

Getting the chosen option from a dropdown list mapped in ReactJS

I am working on a dropdown select option that is linked to the data of an array object called 'template_titles'. Currently, the value in the dropdown corresponds to the title in the object. My goal is to be able to extract and use the selected va ...

producing imperfections on tiny items

I am currently working on rendering a texture onto a cylinder using threeJS. While it usually works well, I have encountered some strange artifacts when the radius of the cylinder is set to very low values (such as 0.0012561892224928503 in the image attac ...

JavaScript Drag Events in Microsoft Edge (Including IE)

My drag event is functioning properly in Chrome, Safari, Firefox, and Opera. However, I encountered an error when running it on Microsoft Edge and IE: SCRIPT438: Object doesn't support property or method 'setDragImage' Below is the code sn ...

When trying to make a POST request, the browser displayed an error message stating "net::ERR_CONNECTION

Currently, my project involves coding with React.js on the client side and Express.js on the server side. I have encountered an issue when attempting to use the POST method to transmit data from the client to the server for storage in a JSON file. The erro ...

Encountering an unusual reactivity problem involving Firebase (Firestore) when using Vue.js and Vuefire

I'm facing a strange issue and I'm completely stuck. Here is the component in question: <template> <v-card elevation="0"> <h2>Accounts</h2> <v-simple-table fixed-header height="300px"> <template v ...

Leveraging useContext to alter the state of a React component

import { createContext, useState } from "react"; import React from "react"; import axios from "axios"; import { useContext } from "react"; import { useState } from "react"; import PermIdentityOutlinedIcon f ...

Using CSS in combination with AngularJS, you can create a dynamic growth effect for a div element that initially

I am currently using AngularJS to dynamically add divs to a section. My goal is to have each div start with a static width and then grow dynamically as I continue to add more divs. How can I achieve this? The following code is not producing the desired re ...

Just SSR / turn off client-side rendering

<template> <nav v-once> <catalog-menu-container v-once :items="this.awd.children_data" /> </nav> </template> <script> import axios from 'axios'; import catalogMenuContainer from '~/components/cat ...

Transmitting intricate Javascript Array to ASP.NET Controller Function

I am facing an issue with sending a complex JavaScript array to my asp.net mvc6 controller method. I have tried two different methods to pass the data, but neither seem to be working for me. public IActionResult TakeComplexArray(IList<ComplexArrayInfo ...

The AngularJS price slider may exceed its range if the ng-model is null or below the minimum value

I currently have an rz-slider featured on my webpage that is utilized for gathering the price of a product from the user. In addition to the slider, there are two input fields present which are designated for storing the minimum and maximum values. The ng- ...

Is the imported style file not properly scoped?

Whenever I attempt to import a CSS file using this method, the styling is not properly scoped. Is it necessary to write the styles within a style tag for them to work correctly? I have been experimenting with this in Vue-cli. <style scoped> @im ...

Effective ways to transfer data between services and controllers

Is there a way to pass values from services to controllers effectively? Despite researching on stackoverflow, I haven't found a solution that addresses my issue. My goal is to access google spreadsheets using tabletop.js. Interestingly, when I log val ...

Is there a way to determine which radio button has been chosen using jQuery?

I'm trying to retrieve the value of the selected radio button using jQuery. Can anyone help with this? Currently, I am able to target all radio buttons like so: $("form :radio") But how can I determine which one is actually selected? ...

What is the best way to generate a new Object using an Object that contains Arrays?

I currently have a global array saved with a catalog and a list of items that the user has saved. My task is to generate a new array of Objects (with arrays) containing only the items saved by the user. I am working with javascript in react-native, and I ...