preface

I don’t know if you’ve ever encountered a situation where a list is rendered while its children are still lists. If you have a few levels, you can do it with a few for loops, but if you have a lot of levels or a lot of levels, it’s a little confusing.

In fact, this is a tree structure of data, such as the common organizational chart, folder directories, navigation menus, etc. Many component libraries have tree components, but often the style is not what we want, and it is very difficult to change. So, how do you render this data yourself? The answer is component recursion!

Results show

So that’s using component recursion and adding some interaction. Clicking on a node outputs the object corresponding to the node in the console, and expands or unrolls the child nodes if there are any. Let’s take a look at how to achieve the above effect.

Render complete data

Rendering the data is simple: first, encapsulate the tree structure into a list of components, then determine if each item has child nodes, and if so, use its own components to render it.

src/components/myTree.vue

<template>
  <div class="tree-item">
    <div v-for="item in treeData" :key="item.id">
      <div class="item-title">{{ item.name }}</div>
      <div v-if="item.children && item.children.length" class="item-childen">
        <my-tree :treeData="item.children"></my-tree>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'myTree'.props: {
    treeData: {
      type: Array.default: () = >[]}}}</script>

<style lang="scss" scoped>
.tree-item {
  .item-title {
    padding: 4px 8px;
  }
  .item-childen {
    padding-left: 20px; }}</style>
Copy the code

src/App.vue

<template>
  <my-tree :tree-data="treeData"></my-tree>
</template>

<script>
const treeData = [
  { id: 1.name: 'level 1' },
  {
    id: 2.name: 'level 2'.children: [{id: 3.name: 'secondary 2-1' },
      { id: 4.name: 'secondary 2-2'}]}, {id: 5.name: 'level 3'.children: [{id: 6.name: 'secondary 3-1'.children: [{id: 7.name: 'triple the 3-1-1' },
          { id: 8.name: 'triple the 3-1-2'}]}, {id: 9.name: 'secondary 3-2' },
      { id: 10.name: 'secondary 3-3'}}]]import myTree from '@/components/myTree.vue'
export default {
  components: {
    myTree
  },
  data() {
    return {
      treeData: treeData
    }
  }
}
</script>
Copy the code

The effect is as follows:

Obtaining Node Data

First we use $emit to send the item to the first level node when clicking on it.

$emit(‘node-click’, $event) @node-click=”$emit(‘node-click’, $event)”; So each child can call the parent’s node-click method, and the parent can call its parent’s Node-click method, and finally call the outermost node-click method. We just need to pass the data in the process. This is a little bit convoluted, I believe you should be able to see it a few times. Modified as follows:

src/components/myTree.vue

<div class="item-title" @click="itemNodeClick(item)">{{ item.name }}</div>
<div v-if="item.children && item.children.length" class="item-childen">
  <my-tree
    :treeData="item.children"
    @node-click="$emit('node-click', $event)"
  ></my-tree>
</div>.itemNodeClick(item) {
  this.$emit("node-click", item)
}
Copy the code

src/App.vue

<my-tree :tree-data="treeData" @node-click="nodeClick"></my-tree>
...
nodeClick(val) {
  console.log(val)
}
Copy the code

Results the following

Dynamic unfolding and folding

The idea of this step is to set up an array for the component. The array stores the ids of the nodes that need to be expanded in the current list. When the node is clicked, add or delete the node ID, and then determine whether the id of each node is in the array.

src/components/myTree.vue

<div class="item-title" @click="nodeClick(item)">
  <span>{{ item.name }}</span>
  <span v-if="item.children && item.children.length">
    [{{ isOpen(item.id) ? '-' : '+' }}]
  </span>
</div>
<div
  v-if="item.children && item.children.length"
  v-show="isOpen(item.id)"
  class="item-childen"
>
  <my-tree
    :treeData="item.children"
    @node-click="$emit('node-click', $event)"
  ></my-tree>
</div>.data() {
  return {
    expandedKeys: [] // An array of node ids to expand in the current list}},methods: {
  nodeClick(item) {
    this.$emit('node-click', item)
    if (item.children && item.children.length) {
      let index = this.expandedKeys.indexOf(item.id)
      if (index > -1) {
        // If the current node id exists in the array, delete it
        this.expandedKeys.splice(index, 1)}else {
        // If the current node ID does not exist in the array, add it
        this.expandedKeys.push(item.id)
      }
    }
  },
  isOpen(id) {
    // Check whether the node id is in the array, if yes, display, not hide
    return this.expandedKeys.includes(id)
  }
}
Copy the code

Results the following

Finally we add some styles and we’re done!

The complete code

src/components/myTree.vue

<template>
  <div class="tree-item">
    <div v-for="item in treeData" :key="item.id">
      <div class="item-title" @click="nodeClick(item)">
        <span>{{ item.name }}</span>
        <span v-if="item.children && item.children.length">
          [{{ isOpen(item.id) ? '-' : '+' }}]
        </span>
      </div>
      <div
        v-if="item.children && item.children.length"
        v-show="isOpen(item.id)"
        class="item-childen"
      >
        <my-tree
          :treeData="item.children"
          @node-click="$emit('node-click', $event)"
        ></my-tree>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'myTree'.props: {
    treeData: {
      type: Array.default: () = >[]}},data() {
    return {
      expandedKeys: [] // Array of node ids currently expanded}},methods: {
    nodeClick(item) {
      this.$emit('node-click', item)
      if (item.children && item.children.length) {
        let index = this.expandedKeys.indexOf(item.id)
        if (index > -1) {
          // If the current node id exists in the array, delete it
          this.expandedKeys.splice(index, 1)}else {
          // If the current node ID does not exist in the array, add it
          this.expandedKeys.push(item.id)
        }
      }
    },
    isOpen(id) {
      // Check whether the node id is in the array, if yes, display, not hide
      return this.expandedKeys.includes(id)
    }
  }
}
</script>

<style lang="scss" scoped>
.tree-item {
  cursor: pointer;
  .item-title {
    padding: 4px 8px;
    &:hover {
      background: #eee; }}.item-childen {
    padding-left: 20px; }}</style>
Copy the code

src/App.vue

<template>
  <my-tree :tree-data="treeData" @node-click="nodeClick"></my-tree>
</template>

<script>
const treeData = [
  { id: 1.name: 'level 1' },
  {
    id: 2.name: 'level 2'.children: [{id: 3.name: 'secondary 2-1' },
      { id: 4.name: 'secondary 2-2'}]}, {id: 5.name: 'level 3'.children: [{id: 6.name: 'secondary 3-1'.children: [{id: 7.name: 'triple the 3-1-1' },
          { id: 8.name: 'triple the 3-1-2'}]}, {id: 9.name: 'secondary 3-2' },
      { id: 10.name: 'secondary 3-3'}}]]import myTree from '@/components/myTree.vue'
export default {
  components: {
    myTree
  },
  data() {
    return {
      treeData: treeData
    }
  },
  methods: {
    nodeClick(val) {
      console.log(val)
    }
  }
}
</script>
Copy the code

Results the following

That’s all for today’s sharing! If you are interested, you can try it on yourself, encapsulate the component further, or modify it to your own style. Here is the official tree view of Vue:

Cn.vuejs.org/v2/examples…

If it helps, don’t forget to give it a thumbs up! Wish everyone a happy New Year in advance! Love and business thrive