【问题标题】:Vue: Prop Value change not updating DOMVue:道具值更改不更新 DOM
【发布时间】:2021-04-01 01:53:51
【问题描述】:

我有一个子组件

<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>

比我在父组件中使用这个组件如下

<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>

每当我需要在事件处理程序中选择这样的活动项时,我都会更改 treeDataUiHelpers 的值

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);
        }
      },

现在,我花了几个小时查看 stackoverflow 和 vue js 论坛,并尝试了所有解决方案。但是当我更改treeDataUiHelpers 的值时,我无法更新 dom。另外,treeDataUiHelpers 的手表也永远不会触发,无论我以哪种方式更改它在父级中的值。任何帮助将不胜感激。

PS,我可以在开发工具中看到 prop 中的值已更新,但 dom 未更新,计算值也未更新。

【问题讨论】:

  • 这些 UI 助手是否用于在树上显示常用工具栏图标?比如编辑、删除等?
  • 目前这些仅用于显示树中项目的级别和切换树项目的活动类。我只是不想弄乱 api 发送的对象,所以我为 ui 创建了一个辅助对象。
  • 某处有 git repo 吗?
  • 不,不可能,。

标签: vue.js vuejs2 vue-component vue-props


【解决方案1】:

好吧,Props 是不可变的,或者你可以说它具有单向数据流。

解决此问题的一种方法是订阅 treeItem 组件中的事件,然后在 treeDataUiHelpers 更改时发出该事件。

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

EventBus 是一个全局的,类似于

EventBus = new Vue({});

但如果你有更多这样的场景,那么更喜欢 Vuex 用于组件之间的数据共享。

【讨论】:

  • 但是为什么我必须为此使用事件总线。 props 从子组件是不可变的,从所有者(即父组件)改变它们并没有错。
  • 我的简单问题是道具是反应性的,它在任何地方都非常清楚地说明了,那么为什么当父更改道具对象中的属性时不重新计算计算值。另外,为什么在更改时不触发监视功能。
  • 不,你没有正确理解它。我不想改变孩子的数据。我想更改来自父级的数据并确保它在数据更改时更新 dom。我知道数据会向下流动,但这不是这里的问题,问题是反应性。
  • 哦,误会了 :(。刚刚滚动发现 // 不工作 cmets。
【解决方案2】:

如果试图复制这种情况并在 setActive() 中应用双 $set: 在我的示例中(我可以根据要求提供),我从一个空的 treeDataUiHelpers 对象开始,以证明双重设置是有效的。所选属性将被所有树项节点检测到。

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

如果我查看您的 setActive 函数,我会看到您尝试这样做


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

这看起来很奇怪,如果 helperItem.selected 是 true-ish,尝试将其设置为 true?

【讨论】:

  • 抱歉,这行实际上是if(!helperItem.selected)。我会更新问题
  • " 我从一个空的 treeDataUiHelpers 对象开始,以证明双重集是有效的" Reagrding this。我不需要证明这一点,因为我确定对象在那里,因为我在创建对象的已创建钩子上添加了节点级别
  • 好吧,这似乎是一个很难远程解决的问题。也许您可以提供有关父组件的更多上下文或提供代码不起作用的工作示例(jsfiddle、codesandbox 等)。
  • 好的,我会尝试为它拼凑一个小提琴。也许它可以以更好的方式阐明
猜你喜欢
  • 1970-01-01
  • 2021-10-03
  • 2020-01-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-15
  • 1970-01-01
  • 2018-01-10
相关资源
最近更新 更多