v-treeview не обновляет дочерние узлы динамически - PullRequest
0 голосов
/ 10 июля 2020

enter image description here

I have a v-treeview on the left, and the JSON data the treeview uses displayed on the right. When creating root child nodes, the treeview updates correctly. However, when adding a child node under the root (you can see the object correctly updates on the right), the treeview does not automatically capture that and render a child under the root element.

When updating the object data, the entire object that the treeview references gets rebuilt, so there should not be a circumstance where array manipulation becomes an issue. I am not simply doing a push on a child.

Is there any way around this? Using this.$forceUpdate() also does not work.

So far, the only very hacky solution is to set the array to [], then set it to the data after a timeout

this.treeViewData= []

setTimeout(() => {
   this.treeViewData = realData

   this.busy = false
}, 400)

But that's not great


The folder structure is just a flat array:

  fileTree: [
    {
      id: 1,
      parentId: null,
      folderName: 'My Documents'
    },
    {
      id: 2,
      parentId: 1,
      folderName: 'Root Folder 1'
    },
    {
      id: 3,
      parentId: 1,
      folderName: 'Root Folder 2'
    },
    {
      id: 4,
      parentId: 2,
      folderName: 'Child 1 of Root Folder 1'
    },
    {
      id: 5,
      parentId: 2,
      folderName: 'Child 2 of Root Folder 1'
    },
  ]

Which gets converted to the tree object on every change using:

    getFolderStructure() {
      const idMapping = this.fileTree.reduce((acc, el, i) => {
        acc[el.id] = i
        return acc
      }, {})

      let root = {}

      for (const el of this.fileTree) {
        el.children = []
        if (el.parentId === null) {
          root = el
          continue
        }
        // Use our mapping to locate the parent element in our data array
        const parentEl = this.fileTree[idMapping[el.parentId]]
        // Add our current el to its parent's `children` array
        parentEl.children = [...(parentEl.children || []), el]
      }

      this.folderStructure = {}
      this.folderStructureChildren = []

      setTimeout(() => {
        this.folderStructure = root
        this.folderStructureChildren = root.children
      }, 10)
    },

So, when adding a new folder, I just add a new object to the array (with whatever parent ID I want), and that gets generated to the object.

For completeness sake, I followed this tutorial: https://typeofnan.dev/an-easy-way-to-build-a-tree-with-object-references/

Ответы [ 2 ]

1 голос
/ 11 июля 2020

Чтобы правильно отобразить это без каких-либо дополнительных манипуляций, каждый элемент в массиве fileTree должен иметь свойство children:[]

0 голосов
/ 10 июля 2020

Найдите приведенный ниже фрагмент, в котором я пытался воспроизвести ваш сценарий.

new Vue({
  el: "#app",
  vuetify: new Vuetify(),
  data: () => ({
    tree: [],
    items: [
      {
        id: 1,
        parentId: null,
        folderName: "My Documents"
      },
      {
        id: 2,
        parentId: 1,
        folderName: "Root Folder 1"
      },
      {
        id: 3,
        parentId: 1,
        folderName: "Root Folder 2"
      }
    ]
  }),
  mounted() {
    Array.from(this.items).map((it, i) => {
      this.$set(this.items[i], 'children', [])
    });
  },
  methods: {
    addFolder: function () {
      const _id = Math.floor(Math.random() * 100) + 5;
      this.items.push({
        id: _id,
        folderName: `New Dummy ${_id}`,
        parentId: 1
      });
    },

    addSubFolder: function (id) {
      let subId = Math.floor(Math.random() * 1000) + 100, obj = {
          id: subId,
          folderName: `Sub Folder ${subId} of Root Folder ${id}`,
          parentId: id,
          noSub: true
        }, child = Array.from(this.items).filter((it) => it.id === id)[0].children;
      child.push(obj)
    }
  }
});
<head>
  <link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
  <link href="https://cdn.jsdelivr.net/npm/@mdi/font@5.x/css/materialdesignicons.min.css" rel="stylesheet">
  <link href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
</head>

<script src="https://cdn.jsdelivr.net/npm/vue@2.x/dist/vue.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js"></script>
  
  
<div id="app">
  <v-app id="inspire">
    <v-treeview v-model="tree" :items="items" activatable item-key="folderName" item-text="folderName" open-on-click>
      <template v-slot:prepend="{ item, open }">
        <v-icon>
          {{ open ? 'mdi-folder-open' : 'mdi-folder' }}
        </v-icon>
      </template>
      <template v-slot:append="{ item, open }">
        <v-btn color="error" @click.stop="addSubFolder(item.id)" v-if="!item.noSub">
          Add Sub Folder
        </v-btn>
      </template>
    </v-treeview>
    <v-btn @click="addFolder" color="primary">Add Main Folder</v-btn>
  </v-app>
</div>
...