0
votes

I have a child component

<script type="text/x-template" id="item-template">
  <li>
    <div
      class="item"
      :class="{active: selected}"
      @click="toggle">
      <span :style="margin">
        <i v-if="isFolder && !isOpen" class="fas fa-folder"></i>
        <i v-if="isFolder && isOpen" class="fas fa-folder-open"></i>
        <i v-if="!isFolder" class="fas fa-file"></i>
        {{ item.name }}
      </span>
      <b-dropdown no-caret>
        <template #button-content>
          <i class="fas fa-ellipsis-v"></i>
          <i class="fas fa-chevron-down"></i>
        </template>
        <b-dropdown-item v-if="isFolder" @click="$emit('show-add-file-modal', item.path)"><i class="fas fa-file-medical pr-1"></i> New File</b-dropdown-item>
        <b-dropdown-item v-if="isFolder" href="#"><i class="fas fa-file-upload pr-1"></i> Upload File</b-dropdown-item>
        <b-dropdown-item v-if="isFolder" @click="$emit('show-add-directory-modal', item.path)"><i class="fas fa-folder-plus pr-1"></i> New Directory</b-dropdown-item>
        <b-dropdown-divider v-if="isFolder"></b-dropdown-divider>
        <b-dropdown-item @click="$emit('rename-item', item)"><i class="fas fa-pencil-alt pr-1"></i> Rename</b-dropdown-item>
        <b-dropdown-item @click="$emit('delete-item', item)"><i class="fas fa-trash pr-1"></i> Delete</b-dropdown-item>
      </b-dropdown>
    </div>
    
    <ul v-show="isOpen" v-if="isFolder">
      <tree-item
        v-for="(child, index) in item.children"
        :key="index"
        :item="child"
        :tree-data-ui-helpers="treeDataUiHelpers"
        @set-active="$emit('set-active', $event)"
        @show-add-file-modal="$emit('show-add-file-modal', $event)"
        @show-add-directory-modal="$emit('show-add-directory-modal', $event)"
        @rename-item="$emit('rename-item', $event)"
        @delete-item="$emit('delete-item', $event)"
      ></tree-item>
    </ul>
  </li>
</script>

<script>
  Vue.component("tree-item", {
    template: "#item-template",
    props: {
      item: {
        type: Object,
        required: true,
      },
      treeDataUiHelpers: {
        type: Object,
        required: true,
      }
    },
    data: function() {
      return {
        isOpen: false,
        isSelected: false
      };
    },
    computed: {
      treeDataUiHelpersComputed: function() {
        console.log(this.treeDataUiHelpers);
        return this.treeDataUiHelpers;
      },
      isFolder: function() {
        return this.item.type === 'DIRECTORY';
      },
      margin: function() {
        return {"margin-left": (this.treeDataUiHelpers[this.item.path].level * 16) + 'px'};
      },
      selected: function() {
        console.log(this.treeDataUiHelpersComputed[this.item.path].selected);
        return this.treeDataUiHelpersComputed[this.item.path].selected
      },
    },
    watch: {
      treeDataUiHelpers: {
        handler(value) {
          console.log(value);
        },
        deep: true
      },
    },
    methods: {
      toggle: function() {
        if (this.isFolder) {
          this.isOpen = !this.isOpen;
        }
        else {
          this.$emit("set-active", this.item);
        }
      },
    }
  });
</script>

Than i use this component as following in the parent component

<tree-item
  :item="treeData"
  :tree-data-ui-helpers="treeDataUiHelpers"
  @set-active="setActive"
  @show-add-file-modal="showAddFileModal"
  @show-add-directory-modal="showAddDirectoryModal"
  @rename-item="renameItem"
  @delete-item="deleteItem"
></tree-item>

Than i am changing the value of treeDataUiHelpers whenever i need to select the active item like this in the event handler

setActive: function(item) {
        if(!item) {
          this.setAllInActive();
          return;
        }
        const helperItem = this.treeDataUiHelpers[item.path];
        if(!helperItem.selected){              
          this.setAllInActive();
          //helperItem.selected = true; // not working
          //this.treeDataUiHelpers[item.path] = {...this.treeDataUiHelpers[item.path], selected:true};// not working
          Vue.set(this.treeDataUiHelpers[item.path], 'selected' , true); //not working
          //this.treeDataUiHelpers[item.path]= Object.assign({}, this.treeDataUiHelpers[item.path], {selected:true})// Not working
          console.log(this.treeDataUiHelpers[item.path]);
          this.setOpenedFilesActive( item.name, item.path);
          const contentItem = this.treeDataContent[item.path];
          const model = this.monaco.editor.createModel(
            contentItem.content,
            contentItem.type
          );
          this.editor.setModel(model);
        }
      },

Now, I have spent hours looking on stackoverflow and vue js forums and tried every solution there was. But i cannot get the dom to update when i change value of treeDataUiHelpers. Plus the watch for treeDataUiHelpers is also never firing no matter in which way i change the value of it in the parent. Any help would be appreciated.

PS, i can see in the dev tools that value is updated in the prop but dom is not updating and also computed values are not updating too.

2
Are these UI helpers used for display common toolbar icons on the tree? like edit, delete etc? - kasvith
For now these are just for showing the level of the item in the tree and switching the active class of a tree item. I just don't want to mess up the object sent by the api so i created a helper object along with it for ui. - Azzam Asghar
Is there a git repo somewhere? - Riza Khan
No not possible for it,. - Azzam Asghar

2 Answers

0
votes

Well, Props are immutable or you can say it has one-way data flow.

The one solution to this could be to subscribe to an event in treeItem component and then emit that event on treeDataUiHelpers change.

Vue.component("tree-item", {
    ...
created(){ 
      EventBus.$on('data-ui-helper-change', updatedTreeDataUiHelpers => {
      this.treeDataUiHelpers = updatedTreeDataUiHelpers 
  } ) 
},
data(){ return { treeDataUiHelpers = null } }
....

EventBus is a global, something like

EventBus = new Vue({});

But if you have more scenario like this then prefer Vuex for data sharing between the components.

0
votes

If tried to replicate the situation and applied a double $set in setActive(): In my example (which I can supply on request) i start out with an empty treeDataUiHelpers object to proof that the double-set is effective. The selected property will be detected by all the tree-item nodes.

setActive(item) { 
    this.$set(this.treeDataUiHelpers, item.path, this.treeDataUiHelpers[item.path] || {});
    this.$set(this.treeDataUiHelpers[item.path], 'selected', true);
}

If I look at your setActive function I see you try to do


if(helperItem.selected) {
     ...
     // various attempts to set treeDataUiHelpers selected to true
     ..
     Vue.set(this.treeDataUiHelpers[item.path], 'selected' , true); //not working
}

Which seems odd, if helperItem.selected is true-ish, try to set it to true?