The cause of

The thing is, there’s been a demand on the project recently. The server has an image space, which is basically a folder. You know the structure of the folder, layer by layer. And then you need a tree at the front.

The specific requirements are as follows

  • You can create subdirectories, but you can only create second, third, new button is disabled.
  • To display the parent directory name and current path on the page, use'/'Space.
  • The root directory is fixed to image space.
  • When the page is opened for the first time, the interface is requested and only the level 1 directory is returned. By default, all level 1 directories are displayed.
  • The user clicks on the level 1 directory, according to the click level 1 directory corresponding toidThe request interface returns the secondary directory, and so on.

When I saw this requirement, I thought to myself, I’ve seen this on the ElementUI website, good. It turned out that when I went to develop it, it was quite complicated, so I spent some time researching it in case I forgot to use it next time.

To deal with

Let’s copy the official elementUI example. And then I added something random.


<template>
  <div>
    <el-input v-model="input"
              style="width:200px"
              placeholder="Please enter content"></el-input>

    <el-button type="primary">Add a node</el-button>

    <span>Current directory:</span>

    <span>Parent directory of the current directory:</span>

    <el-tree :data="data"
             :props="defaultProps"
             accordion
             @node-click="handleNodeClick"></el-tree>
  </div>
</template>
<script>
export default {
    data() {
    return {
      input: ' '.data: [{
        label: 'Picture Space'.children: [{
          label: 'vegetables'.children: [{
            label: 'spinach'}, {label: 'lettuce',},],}, {label: 'fruit'.children: [{
            label: 'banana',}],}, {label: 'beauty'.children: [{
            label: 'Qin Lan',}],}],}],defaultProps: {
        children: 'children'.label: 'label',}}; },methods: {
    handleNodeClick(data) {
      console.log(data); ,}}};</script>

Copy the code

Now the interface looks like this.

Demand a

Let’s start with the simplest. Add a category (child node) to any directory.

Then we go and see what the documentation says. Well, this is it.

Receive two parameters (data, parentNode)

  • Of the child node to be appendeddata
  • The child nodes of theparentdata,keyornode

The first parameter, data, is just a name in the current example. How do I get the second parameter that I want to pass?

To add a child node to a directory, you have to click on the directory first, so let’s see what click events give us back.

You can see that the second callback parameter is the node corresponding to the node, so there you have it! So give it a try.

When we click on a node, the node callback is stored in the nowClickNode variable. When we call append, we pass nowClickNode to it.

// To save space, omit the code in the template and post the whole code at the end
methods: {
    // Click the node
    handleNodeClick(nodes, node, self) {
      this.nowClickNode = node;
    },
    // Add a node
    addSort() {
      const data = {
        label: 'strawberry'};this.$refs.tree.append(data, this.nowClickNode); }},Copy the code

Success!

NowClickNode: nowClickNode: nowClickNode: nowClickNode: nowClickNode: nowClickNode: nowClickNode This variable has a level attribute, which represents the level. I do this by calculating properties.

//template
<el-button type="primary"
           :disabled='isDisabled'
           @click='addSort'> Add a node </el-button>//js
computed: {
    // Whether to create a new category
    isDisabled() {
      if (this.nowClickNode.level > 2) {
        return true;
      }
      return false; }},Copy the code

You can see that the button is disabled when we hit level three.

Demand for two

Next we implement the second requirement, showing the current path and parent directory. Just look at the one above for this example. It’s already implemented.


// Triggers an event when a node is clicked
handleNodeClick(nodes, node, self) {
  this.nowClickNode = node;
  this.nowPathArr = [];
  // If not the root node
  if (node.parent.parent) {
    this.faterName = node.parent.data.label;
    // Find the path recursively
    this.findParentName(node);
    // Splice paths
    this.nowPath = 'Picture space /The ${this.nowPathArr.join('/')}`;
  } else {
  // Assign a value directly to the root node
    this.nowPath = 'Picture Space';
    this.faterName = '-'; }},// Find the class name of the parent node
findParentName(node) {
  if (node.parent.parent) {
    this.nowPathArr.unshift(node.data.label);
    this.findParentName(node.parent); }},Copy the code

My root directory also has the parent attribute, so I have to go up one level to see if parent. Parent exists.

What I’m doing is I’m recursively looking for the parent element of the node that I clicked on, and then I’m putting all the ancestors in the array, and THEN I’m splicing.

Three, four, five

The remaining three requirements are put together. In this place, I used the real data of the project when I practiced, which is not convenient to demonstrate. The code is also desensitized. The important thing is to know how to use it.

//template
<el-tree 
     :data="treeData"  / / the data source
     :props="defaultProps"  / / configuration items
     accordion // Open accordion mode one node at a time
     ref="tree" // To get the DOM
     lazy  // The node is loaded lazily
     :highlight-current='true' // Highlight the current node
     :load='treeLoad' // The method called during lazy loading
     node-key="cateId" // Unique identifier
     :default-expanded-keys='defaultOpen'  // The array of nodes opened by default
     :expand-on-click-node='true' // Whether to expand or shrink a node when clicked. The default value is true. If false, the node will be expanded or shrunk only when the arrow icon is clicked.
     @node-click="handleNodeClick" // Click the callback function of the node
     >
</el-tree>

//js
// The load tree is the function that lazily loads the binding
treeLoad(node, resolve) {
  // Determine the node level
  if (node.level == 0) {
    // If it is the root node, add a fixed image space node
    resolve([{
      cateName: 'Picture Space'.cateId: '0'.children: [],}]); }// Not the root node, fetch the interface load list here to realize the need for four, five tree structure lazy load, click the end of the level request level data
  if (node.level >= 1) {
    this.getCategoryListNew(node, resolve);
    // Prevent the loading animation from appearing all the time when the child node is empty
    returnresolve([]); }},// Call the resolve method after data is processed
getCategoryListNew(node, resolve) {
  Service.getCategoryListNew({ parentId: node.data.cateId }).then(({ data }) = > {
      resolve(data);
  }).catch((e) = > {
    console.log(e);
  });
},
Copy the code

Other knowledge points

Note a few points of knowledge

Default expansion node

The default expansion is default-expanded-keys=’defaultOpen’. DefaultOpen stores an array of Node-keys.

Horizontal and longitudinal scroll bars

First, add width and height to the outer layer of the Tree component, as well as overflow properties. Then you need to modify the properties of el-Tree.

.tree-box{
  width: 200px;
  height: calc(100% - 30px);
  overflow:auto;
}
.el-tree {
  display: inline-block;
  min-width: 100%;
}
Copy the code

Refreshes a node

// Find the corresponding tree node object by node ID
const node = this.$refs.tree.getNode(this.nodeId);
// Set loaded to false to tell the tree that the node was not loaded
node.loaded = false;
// Call the expand node method to query all child nodes under this node
node.expand();
Copy the code

Simulate clicking on the first node in the level 1 directory

/ / select the dom
const root = document.querySelector('.el-tree-node__children');
// Find the target node
const firstNode = root.firstChild;
// Simulate the click
firstNode.click();
Copy the code