Expand v-treeview based on filtered results

Viewed 7460

In the tree view component, I would like to open all the nodes where there is some search text. But the expected is not happening.

Desired output: Open all the parents where there's some search text. enter image description here Here's the codepen for the same.

https://codepen.io/anon/pen/MdxPKN?&editors=101

<div id="app">
  <v-container grid-list-md>
    <v-layout wrap>
      <v-flex xs6>
        <v-text-field label="search" v-model="search" box />

        <v-treeview :items="tree"
          :search="search"
          active-class="grey lighten-4 indigo--text"
          item-key="name"
          open-on-click
          :open-all="{searchLength}>0?true:false"
          hoverable />
      </v-flex>
    </v-layout>
  </v-container>
</div>
4 Answers

So it got a little tricky, you can't use the builtin search functionality, but there's an acceptably easy workaround.

You basically have to implement the filter yourself, and just send the items you need to v-treeview.

Then you can create another computed property from your filteredElements which just return the key and pass it to the :open property of treeview.

Made a codepen for you.

https://codepen.io/brafols/pen/XwGQov

Here is an approach based off your example that works with the built-in search.

https://codepen.io/totalhack/pen/KKzzXvr

I added a search input event handler that uses the treeview updateAll function to open all items when a search starts. It then returns to the previous open state when the search text is empty. Note that if you use the built-in clearable prop of v-text-field you may need to handle events from that too (I haven't tried it).

<div id="app">
  <v-app id="inspire">
    <v-container>
      <v-layout>
        <v-flex xs6>        
          <v-text-field
            label="Search"
            v-model="search"
            @input="handleSearch"
            >         
          </v-text-field>
          <v-treeview 
            ref="tree"
            :items="tree"
            :search="search"
            :open.sync="open"
            open-on-click
            hoverable>
          </v-treeview>
        </v-flex>
      </v-layout>
    </v-container>
  </v-app>
</div>
new Vue({
  el: '#app',
  vuetify: new Vuetify(),
  data(){
    return{
      search: '',
      open: [1],
      allOpened: false,
      lastOpen: [],
      tree: [
        {
          id: 1,
          name: 'Applications',
          children: [
            { id: 2, name: 'Calendar' },
            { id: 3, name: 'Chrome' },
            { id: 4, name: 'Webstorm' }
          ]
        },
        {
          id: 10,
          name: 'Languages',
          children: [
            { id: 20, name: 'English' },
            { id: 30, name: 'French' },
            { id: 40, name: 'Spannish' }
          ]
        }
        
      ]
    }
  },
  methods: {
    handleSearch: function (val) {
      if (val) {
        if (!this.allOpened) {
          this.lastOpen = this.open;
          this.allOpened = true;
          this.$refs.tree.updateAll(true);
        }
      } else {
        this.$refs.tree.updateAll(false);
        this.allOpened = false;
        this.open = this.lastOpen;
      }
    }
  }
})

I modified totalhack's solution slightly to get what I wanted. Basically if there is a string in the search box I call updateAll(true).

<template>
<v-card>
  <v-card-title>File Open</v-card-title>
  <v-sheet class="pl-4 pr-4">
    <v-text-field
      label="Search"
      v-model="search"
      @input="handleSearch"
      flat
      solo-inverted
      hide-details
      clearable
      clear-icon="mdi-close-circle-outline"
    ></v-text-field>
  </v-sheet>
  <v-card-text>
    <v-container>
      <v-treeview
        ref="tree"
        :items="tree"
        :search="search"
        open-on-click
        return-object
        activatable
        dense
      >
        <template v-slot:prepend="{ item, open }">
          <v-icon v-if="!item.file">
            {{ open ? 'mdi-folder-open' : 'mdi-folder' }}
          </v-icon>
          <v-icon v-else>
            {{ 'mdi-language-ruby' }}
          </v-icon>
        </template></v-treeview>
    </v-container>
  </v-card-text>
  <v-card-actions>
    <v-btn color="primary" text @click="open()">Ok</v-btn>
    <v-spacer></v-spacer>
    <v-btn color="primary" text @click="show = false">Cancel</v-btn>
  </v-card-actions>
</v-card>
</template>

<script>
export default {
  data() {
    return {
      tree: [],
      search: null,
    }
  },
  methods: {
    handleSearch(input) {
      if (input) {
        this.$refs.tree.updateAll(true)
      } else {
        this.$refs.tree.updateAll(false)
      }
    },
  },
}
</script>

And if you're asking yourself about how to search considering case sensitive, filter still works. I just made this:

filter () {
   return this.caseSensitive ? (item, search, textKey) =>  this.removeAccent(item[textKey].toLowerCase()).indexOf(this.removeAccent(search).toLowerCase()) > -1 : undefined
},

You just need to compare the search query the function without accents etc, with the same but to the item being compared by vuetify tree component internally.

In the component:

<v-treeview ref="tree" v-model="active" selectable :return-object="true" color="primary" selected-color="primary" :items="items" :search="searchOnTree" :filter="filter" @input="selected"></v-treeview>

To get the selected item (vuetify didn't say me it was this event) you just need to assign a function to an input event on the tree.

Related