vue.js –带有快速过滤器的菜单树:可折叠且过滤器无法一起使用

时间:2018-09-19 09:26:26

标签: vue.js vuejs2 vue-component

动机

我想创建一个带有搜索栏的菜单树,以突出显示菜单项。我想出了以下实现:

const treeConfig = [
  {
    label: 'User',
    children: [
      {
        label: 'User group'
      },
      {
        label: 'Permissions'
      }
    ]
  },
  {
    label: 'Tests',
    children: [
      {
        label: 'Unit tests'
      },
      {
        label: 'Visual regression tests'
      }
    ]
  },
  {
    label: 'Other stuff'
  }
];

Vue.component('tree', {
  template: '#tree',
  props: ['nodes'],
  data() {
    return {
      showChildren: true
    };
  },
  methods: {
    toggleChildren() {
      this.showChildren = !this.showChildren;
    }
  }
});

const vm = new Vue({
  el: '#app',
  data: {
    quicksearch: ''
  },
  computed: {
    nodes() {
      const self = this;
      
      function getTree(nodes) {
        return nodes.map((node) => {
          node.label_ = node.label;
          node.label_ = node.label.replace(new RegExp(self.quicksearch, 'gi'), '<mark>$&</mark>');
          if (node.children) {
            node.children = getTree(node.children);
          }
          return node;
        });
      }
      
      return getTree(treeConfig);
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>

<div id="app">
  <input type="search" placeholder="Quick search" v-model="quicksearch">
  <tree :nodes="nodes"></tree>
</div>

<template id="tree">
  <ul>
    <li
      v-for="node in nodes"
    >
    <span
      v-if="node.children"
      @click="toggleChildren()"
    >
      <i v-show="!showChildren">+</i>
      <i v-show="showChildren">-</i>
    </span>
    <span v-html="node.label_"></span>
    <tree
      v-if="node.children"
      v-show="showChildren"
      :nodes="node.children"
    ></tree>
    </li>
  </ul>
</template>

问题

使用上面的代码,子树不能独立折叠。点击+-之一折叠每个子树。

第二种方法

因此,我尝试使用tree-item组件而不是tree组件:

const treeConfig = [
  {
    label: 'User',
    children: [
      {
        label: 'User group'
      },
      {
        label: 'Permissions'
      }
    ]
  },
  {
    label: 'Tests',
    children: [
      {
        label: 'Unit tests'
      },
      {
        label: 'Visual regression tests'
      }
    ]
  },
  {
    label: 'Other stuff'
  }
];

Vue.component('tree-item', {
  template: '#tree-item',
  props: ['node'],
  data() {
    return {
      showChildren: true
    };
  },
  methods: {
    toggleChildren() {
      this.showChildren = !this.showChildren;
    }
  }
});

const vm = new Vue({
  el: '#app',
  data: {
    quicksearch: '',
    title: 'Hello filtered tree!'
  },
  computed: {
    nodes() {
      const self = this;
      
      function getTree(nodes) {
        return nodes.map((node) => {
          node.label_ = node.label;
          node.label_ = node.label.replace(new RegExp(self.quicksearch, 'gi'), '<mark>$&</mark>');
          if (node.children) {
            node.children = getTree(node.children);
          }
          return node;
        });
      }
      
      return getTree(treeConfig);
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>

<div id="app">
  <input type="search" placeholder="Quick search" v-model="quicksearch">
  <ul>
    <tree-item
      v-for="node in nodes"
      :node="node"
    ></tree-item>
  </ul>
</div>

<template id="tree-item">
  <li>
    <span
      v-if="node.children"
      @click="toggleChildren()"
    >
      <i v-show="!showChildren">+</i>
      <i v-show="showChildren">-</i>
    </span>
    <span v-html="node.label_"></span>
    <ul
      v-if="node.children"
      v-show="showChildren"
    >
      <tree-item
        v-for="node in node.children"
        :node="node"
      ></tree-item>
    </ul>
  </li>
</template>

问题

现在可以分别折叠每个子树,但是quickfilter不起作用。看来nodes对象不是反应性的。

我的思维错误在哪里?任何建议表示赞赏。

2 个答案:

答案 0 :(得分:1)

您应该看一下:Reactivity in Depth 当您通过快速搜索值的更改重新计算出您的计算值时,它不会触发其他组件(在本例中为树项目)的重构,因为它不会深入观察对象。

这里简单的解决方法是,完全影响新的数组映射结果。

const treeConfig = [
  {
    label: 'User',
    children: [
      {
        label: 'User group'
      },
      {
        label: 'Permissions'
      }
    ]
  },
  {
    label: 'Tests',
    children: [
      {
        label: 'Unit tests'
      },
      {
        label: 'Visual regression tests'
      }
    ]
  },
  {
    label: 'Other stuff'
  }
];

Vue.component('tree-item', {
  template: '#tree-item',
  props: ['node'],
  data() {
    return {
      showChildren: true
    };
  },
  methods: {
    toggleChildren() {
      this.showChildren = !this.showChildren;
    }
  },
  watch : {
    node(n) {
      console.log(n)
    }
  },
  created() {
    console.log(this.node);
  }
});

const vm = new Vue({
  el: '#app',
  data: {
    quicksearch: '',
    title: 'Hello filtered tree!',
    nodes : []
  },
  methods : {    
    getNodes() {
      const self = this;
      
      function getTree(nodes) {
        return nodes.map((node) => {
          node.label_ = node.label;
          node.label_ = node.label.replace(new RegExp(self.quicksearch, 'gi'), '<mark>$&</mark>');
          if (node.children) {
            node.children = getTree(node.children);
          }
          return node;
        });
      }
      
      return getTree(treeConfig);
    }
  },
  created(){
    this.nodes = this.getNodes();
  },
  watch : {
    quicksearch(q) {
      this.nodes = this.getNodes();
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>

<div id="app">
  <input type="search" placeholder="Quick search" v-model="quicksearch">
  <ul>
    <tree-item
      v-for="node in nodes"
      :node="node"
    ></tree-item>
  </ul>
</div>

<template id="tree-item">
  <li>
    <span
      v-if="node.children"
      @click="toggleChildren()"
    >
      <i v-show="!showChildren">+</i>
      <i v-show="showChildren">-</i>
    </span>
    <span v-html="node.label_"></span>
    <ul
      v-if="node.children"
      v-show="showChildren"
    >
      <tree-item
        v-for="node in node.children"
        :node="node"
      ></tree-item>
    </ul>
  </li>
</template>

答案 1 :(得分:1)

似乎是数组的问题。只需将:key="node.label_"添加到模板中的tree-item标记中即可。