Search code examples
vue.jsvuexvuetify.js

How to keep the v-treeview state after page refresh?


I am trying to understand how to keep the state of the treeview after a page refresh.

For example, I have a treeview of folders. I click on a folder it should expand.

After that, I can click on that item inside the folder (is a link to a page, therefore the whole page is refreshed.)

The problem is the treeview closes the folders back up again and doesn't keep its previous state.

I need it to be static so even after page reload the treeview does not lose its state.

treeview

This bit of code is my treeview:

 v-list.py-2(v-else, dense)
  v-treeview(v-model="tree", :items="treeData", activatable, :open.sync="openIds", @update:open="getOpenIds", item-key="name", open-on-click)
    template(v-slot:prepend="{item, open}")
      v-list-item(v-if="item.isFolder", link, :href='`/` + item.locale + `/` + item.path', :input-value='path === item.path')
        v-list-item-avatar(size="24", tile)
          v-icon  {{ open ? 'mdi-folder-open' : 'mdi-folder' }}
        v-list-item-title {{item.title}}
      v-list-item.noFolders(v-else, :href='`/` + item.locale + `/` + item.path', :key='`childpage-` + item.id', :input-value='path === item.path')
        v-list-item-avatar(size="24", tile)
          v-icon mdi-text-box
        v-list-item-title {{item.title}}

Also, I should mention that the data is dynamic. I am generating the tree (as an Object) using a method.

async buildTree(items){
  this.$store.commit(`loadingStart`, 'browse-load')
  for(let item of items){
    if(item.isFolder){
      let result = await this.$apollo.query({
        query: gql`
          query ($parent: Int, $locale: String!) {
            pages {
              tree(parent: $parent, mode: ALL, locale: $locale) {
                id
                path
                title
                isFolder
                pageId
                parent
                locale
              }
            }
          }
        `,
        fetchPolicy: 'cache-first',
        variables: {
          parent: item.id,
          locale: this.locale
        }
      })
      item.children = _.get(result, 'data.pages.tree', [])
      for(let kid of item.children){
      if(item.isFolder){
          //kid.name = ""
          kid.children = []
        }
      }
    this.buildTree(item.children)
    console.log(this.treeData )
    }
  }
   this.$store.commit(`loadingStop`, 'browse-load')

And that's actually the method that is supposed to save the tree state to the local storage.

getOpenIds(items){

   const memory = JSON.stringify(items)
    this.sessionIds = sessionStorage.setItem('items2', memory)
    this.openIds = JSON.parse(sessionStorage.getItem('items2'))
  console.log(this.openIds)
},

I have tried the suggestions but for some reason, the tree doesn't react to the local storage. IS there something to do with the fact that the method is async?


Solution

  • You can use one of these 2 possibilities below (there are probably more):

    Local storage

    You can use localStorage to keep a record of all your folders' state.

    Then you can add a computed property to get the data from the local storage if it exists and apply it inside your component.

    Next time you can add a JSfiddle with your example so someone could provide you with a detailed solution :)

    How to use: when someone clicks on a folder you should have a v-on:click handler where you store in local storage the new state

    function onClickHandler(folderName: string, isOpen: boolean) {
      localStorage.setItem(folderName, isOpen);
    }
    

    The next thing you should do is get the state for each folder from local storage and apply it - if it doesn't exist just return false.

    function getFolderState(folderName: string) {
     localStorage.getItem(folderName);
    }
    

    Apply it on the v-list-item element and make sure to pass in the same folder name for both functions.

    Vue dynamic component

    There is a built-in feature in Vue that keeps your component alive instead of dismounting when it's removed from the DOM.

    You can use the keep-alive element to wrap your component. It will result in keeping all the component's state when switching between screens.

    <keep-alive>
      <your-component />
    </keep-alive>