Create a customizable Tree structure that includes checkboxes for each item and features drag

I am currently working on incorporating a Tree view with checkboxes and drag & drop functionality in Vue. However, I am unsure of where to begin.

While I have successfully implemented checkboxes, I am struggling to figure out how to enable the feature where selecting a parent will also select all children, and vice versa.

Additionally, I am facing difficulties with implementing Drag and Drop. I attempted using vue-draggable but I am unsure of how to properly implement it.

The desired outcome is something similar to this:

Here is my sandbox showcasing what I have accomplished so far:

https://codesandbox.io/s/immutable-cookies-8lxxf

Answer №1

I attempted to implement this functionality using Vue and a recursive component. The code snippet below demonstrates how you can select all children from a parent node and utilize vue-draggable for dragging items within the same level. It serves as a good starting point, although some adjustments may be required.

UPDATE:

In order to support drag and drop across multiple levels, I have made modifications to ensure that the draggable elements belong to the same group. While I have tested the concept briefly and it seems to work, further fine-tuning is necessary. Instructions for nested vue-draggable implementation can be found here. Additionally, I have created a fork of the JSFiddle on GitHub which showcases the nesting implementation - feel free to visit this link.

let tree = {
  draggableOptions: {group: 'share'},
  label: 'root',
  selected: false,
  nodes: [
    {
      label: 'item1',
      selected: false,
      nodes: [
        {
          label: 'item1.1',
          selected: false
        },
        {
          label: 'item1.2',
          selected: false,
          nodes: [
            {
              label: 'item1.2.1',
              selected: false
            }
          ]
        }
      ]
    }, 
    {
      label: 'item2',
      selected: false
    }
  ]
}

Vue.component('tree-menu', { 
  template: '#tree-menu',
  props: [ 'nodes', 'label', 'depth', 'selected' ],
  data() {
     return {
       showChildren: false
     }
  },
  computed: {
    iconClasses() {
      return {
        'fa-plus-square-o': !this.showChildren,
        'fa-minus-square-o': this.showChildren
      }
    },
    labelClasses() {
      return { 'has-children': this.nodes }
    },
    indent() {
      return { transform: `translate(${this.depth * 50}px)` }
    }
  },
  methods: {
    toggleChildren() {
       this.showChildren = !this.showChildren;
    
    },
    tickChildren(ev) {
      this.selected = !this.selected;  
      this.tickRecursive(this);
    },
    tickRecursive(node) {
       if(node.nodes && node.nodes.length)
          node.nodes.forEach(x => {
            x.selected = this.selected;
            this.tickRecursive(x);
          });
    }
  }
});

new Vue({
  el: '#app',
  data: {
    tree
  }
})
body {
  font-family: "Open Sans", sans-serif;
  font-size: 18px;
  font-weight: 300;
  line-height: 1em;
}

.container {
  padding-left: 5rem;
  padding-right: 5rem;
  margin: 0 auto;
}

.tree-menu {
  .label-wrapper {
    padding-bottom: 10px;
    margin-bottom: 10px;
    border-bottom: 1px solid #ccc;
    .has-children {
      cursor: pointer;
    }
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js">
</script>
<script src="https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="84f7ebf6f0e5e6e8e1eef7c4b5aab3aab4">[email protected]</a>/Sortable.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Vue.Draggable/2.16.0/vuedraggable.min.js"></script>


<div class="container">
  <h4>Implementation of Tree Structure with checkboxes and drag-and-drop functionality in Vue<br/><small>(Using Recursive Components and Vue Draggable)</small><br /><small><a href="https://stackoverflow.com/questions/60260675/implement-tree-with-checkboxes-and-drag-drop" target="_blank">Refer to StackOverflow question</a></small></h4>
  <div id="app">
   
  <tree-menu 
             :nodes="tree.nodes" 
             :depth="0"   
             :label="tree.label"
             :selected="tree.selected"
             ></tree-menu>
       
 
  </div>
    
</div>


<script type="text/x-template" id="tree-menu">

  <div class="tree-menu">
    <div class="label-wrapper">
      <div :style="indent" :class="labelClasses" @click.stop="toggleChildren">
        <i v-if="nodes" class="fa" :class="iconClasses"></i>
        <input type="checkbox" :checked="selected" @input="tickChildren" @click.stop />
        {{label}}
      </div>
    </div>
    
        <draggable v-model="nodes" :options="{group:{ name:'g1'}}">    
    <tree-menu 
      v-if="showChildren"
      v-for="node in nodes" 
      :nodes="node.nodes" 
      :label="node.label"
      :depth="depth + 1"
      :selected="node.selected"
      :key="node"
    >
    </tree-menu>
            </draggable>

  </div>

</script>

Answer №2

Take a look at this great example. It includes all the necessary components to get started right away.

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

I encountered an error stating "angular not found" when attempting to dynamically load my Angular scripts

My Angular application is running smoothly. However, I found it tedious to include multiple script tags in my HTML documents every time. To simplify this process, I decided to create a small script that would automatically generate the necessary tags for m ...

Displaying live data from a spreadsheet directly on a sidebar

My goal is to extract data from another sheet in the same spreadsheet and present it as a dropdown selection in the sidebar. The code.gs file contains a function called getVisualData() that successfully retrieves the desired list: function getVisualData() ...

Utilizing the outcome of an AJAX call in JavaScript

I am facing an issue with a query that is returning 2 rows. Below is my PHP code snippet: $arr = array(); $stmt = $dbh_conn->prepare("SELECT id,name FROM mytable WHERE id <= 2"); $stmt->execute(); $result = $stmt->fetchAll(); /*Array ( ...

Is there a way to display a React component containing an array that is constantly changing due to an external function?

I am facing a challenge involving a component that needs to render an array of divs. The requirement is to add another div after a certain external function is triggered. This function must be declared outside the component for export purposes. The issue ...

Exporting components as the default from Next.js leads to a forced page refresh whenever there is a change

One issue I encountered involved having an index.tsx file containing all the shared UI components. When I exported the Footer and imported it into my shared Layout.tsx, which is utilized across all pages, the browser would refresh the page every time a cha ...

Guide on accessing text content from a sibling div element using jQuery

There are several divs arranged on a page as shown below. If a user clicks a link within the UL .list-links, I want to capture the content inside: <span class="asa_portlet_title">Title here</span> and store it in a variable. I have attempted ...

How can we create a two-dimensional image with random dimensions using Math.random?

I am struggling to create a variety of flowers with different sizes from an Array. The issue I'm facing is that every time I click to add a new flower, it changes the size of all existing flowers as well. What I would like is for each added flower to ...

Why Changing the Width of a Flexbox Container Doesn't Impact Its Children?

Attempting to use TweenLite to animate the width of the blue sidebar down to zero, however facing an issue where the content breaks outside the parent's bounds. https://i.stack.imgur.com/4rEVr.png It is unusual for this to happen with Flexbox, given ...

Launch a modal from a separate webpage

I've been trying to implement a modal using Bootstrap 4, and after referring to the documentation, it seems to be working smoothly. <link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet"/> <scri ...

JavaScript allows for the creation of animated or timed text

Here is the code snippet I am currently working with: function list() { return "blob1<br>blob2<br>blob3"; } When I run this code, it simply displays all the text in return at once when the function is called. I am wondering if there is a ...

Attempting to deserialize serialized data from Django using JSON.parse

I need assistance with some client-side tasks using Javascript. In my view, I serialize a Queryset from my models into JSON and pass it to the template. data = serializers.serialize("json", Profile.objects.filter(user_id=self.request.user)) This results ...

Looking for a method to substitute "test" with a different value

Showing a section of the menu using <li id="userInfo" role="presentation" data-toggle="tab" class="dropdown"> <a href="#" name="usernameMenu" class="dropdown-toggle" data-toggle="dropdown" role="button"> <span class="glyphicon glyph ...

Tips on how to utilize JavaScript to display data on a page without the need for refreshing, updating every 2-5

Could you please assist me? I am working on a CRUD application and have created a function called loaddata();. My issue is that when another user adds data, it should be displayed in my table without the need to refresh. Is there a way to achieve this? fun ...

After the upgrade from Angular 5.2 to Angular 6, Bootstrap 4 dropdown and Bootstrap-select dropdown seem to have lost their dropdown functionality. This issue arose after updating to Bootstrap 4 and jquery

Update: Upon further investigation, I experimented with a standard Bootstrap 4 dropdown and encountered the same issue – it would not open. This leads me to believe that the problem may not be specific to the selectpicker class or the bootstrap-select de ...

Exploring TypeScript: Optional Sub-Properties

I've been exploring ways to create a type-alias with properties like "answer" that I came across in this post by another user (Typescript interface optional properties depending on other property). Here's an example: type Sample = { key1: true, ...

Creating a dropdown menu in Vue.js with a nested array

Upon receiving a response from the Zoho API, I am faced with an array structured as follows: { "response": { "result": [{ "4076840000001": [ { "Department": "Department 1", " ...

Is there a way to access the active request being processed in a node.js environment?

I am currently working with express.js and I have a requirement to log certain request data whenever someone attempts to log a message. To accomplish this, I aim to create a helper function as follows: function logMessage(level, message){ winston.log(le ...

Retrieve the hidden value buried within multiple layers of classes

I'm having trouble retrieving the value from the pickup-address class. Here is my current code. Can anyone help me identify where I might be going wrong? JS var pickupAddress = $('.timeline-item.active-item > .timeline-status > .pickup-add ...

Submitting information to the server utilizing a POST request through jQuery

What I'm attempting to achieve: I am currently working on sending data to my server using the $.post jQuery shortcut. The issue at hand: It seems that my program is not entering my switch case, possibly due to a problem with sending the action prop ...

Having trouble installing $ npm i mini-css-extract-plugin. Any ideas on what might be causing the issue?

There seems to be an error in my setup. I am using Visual Studio Code with gitBash. $ npm i mini-css-extract-plugin npm ERR! code ERESOLVE npm ERR! ERESOLVE unable to resolve dependency tree npm ERR! npm ERR! While resolving: <a href="/cdn-cgi/l/email-p ...