如何在 Vue.js 插槽中使用条件渲染?

Posted

技术标签:

【中文标题】如何在 Vue.js 插槽中使用条件渲染?【英文标题】:How to use conditional rendering with Vue.js slots? 【发布时间】:2020-11-15 23:51:37 【问题描述】:

如何使用 v-if 的结果在 Vue 中渲染两个不同的组件?如果重要的话,我也在尝试使用v-for 渲染TreeView 时这样做。这是我迄今为止尝试过的。

TreeView.vue:

<template>
    <span>
        <ul v-show="isOpen" v-if="isFolder">
            <p
                v-for="(child, index) in item.children"
                v-bind:item="child"
                v-bind:key="index"
                <span v-if="!child.isFolder">
                    <slot v-bind:individualBookmark="child"></slot>
                </span>
                <span v-else> 
                    <div v-bind:class="bold: isFolder" v-on:click="toggle" v-on:dblclick="makeFolder" v-on:contextmenu="rightClick">
                         child.name 
                        <span v-if="isFolder">[ isOpen ? '-' : '+' ]</span>
                    </div>
                </span>
            </p>
        </ul>
    </span>
</template>

<script>
export default 
  name: "TreeView",
  props: 
    item: Object
  ,
  components: 
  ,
  data: function() 
    return 
      isOpen: false
    
  ,
  computed: 
    isFolder: function() 
      return this.item.children.length > 0;
      //return this.item.children && this.item.children.length;
    
  ,
  methods: 
    toggle: function() 
      if (this.isFolder) 
        this.isOpen = !this.isOpen;
      
    ,
    makeFolder: function() 
      if (!this.isFolder) 
        this.$emit("make-folder", this.item);
        this.isOpen = true;
      
    ,
    rightClick: function() 
      alert("Right click action")
    
  

</script>

App.vueBookmark 标签是我构建的自定义组件,它具有isFolder 计算属性):

<template>
   <div id="app">
      <TreeView v-slot:default="slotProps"
                v-bind:item="treeData"
                v-on:make-folder="makeFolder"
                v-on:add-item="addItem">
        <Bookmark v-bind="slotProps.individualBookmark"></Bookmark>
      </TreeView>
   </div>
</template>

<script>
import Bookmark from './components/Bookmark.vue'
import TreeView from './components/TreeView.vue'
import Vue from 'vue'

export default 
    name: 'App',
    components: 
        Bookmark,
        TreeView
    ,

    data: function() 
        return 
            treeData : 
                    name: "My Tree",
                    children: [
                       name: "hello" ,
                       name: "wat" ,
                      
                        name: "child folder 1",
                        children: [
                          
                            name: "child folder",
                            children: [ name: "hello" ,  name: "wat" ]
                          ,
                           name: "hello" ,
                           name: "wat" ,
                          
                            name: "child folder",
                            children: [ name: "hello" ,  name: "wat" ]
                          
                        ]
                      
                    ]
            
          
    ,
    methods: 
        makeFolder: function(item) 
            Vue.set(item, "children", []);
            this.addItem(item);
        ,
        addItem: function(item) 
            item.children.push(
              name: "new stuff"
            );
        
    

</script>

当我运行它时,出于某种原因,TreeView.vue 中的 v-if="!child.isFolder" 总是评估为 true,因此它会呈现 Bookmark 组件。即使在v-if 应该评估为假的情况下,就像给定treeData 的“子文件夹1”子对象时一样,它仍然评估为真。我感觉问题与我在TreeView.vue 中使用&lt;slot&gt;&lt;/slot&gt; 的方式有关,但我不确定。为什么v-if 总是评估为真而不是假?

问题也可能与我编写isFolder 属性的方式有关。

【问题讨论】:

“你在 Treeview 组件中映射 item 属性并添加该属性”你能解释一下如何做到这一点吗?我不完全明白如何在代码中实现它 【参考方案1】:

Treeview 组件内添加一个名为 computedChildren 的计算属性,该组件通过添加 isFolder 属性来映射 item 属性:

  computed: 
    isFolder: function() 
      return this.item.children.length > 0;
      //return this.item.children && this.item.children.length;
    ,
   computedChildren()
      return this.item.children.map(child=>
           child.isFolder=child.children?child.children.length>0:false //this add the property isFolder

          return child
        )
    
  ,

然后循环如下:

<p
 v-for="(child, index) in computedChildren"
...

要替换插槽,您可以使用 template 元素:

   <TreeView 
                v-bind:item="treeData"
                v-on:make-folder="makeFolder"
                v-on:add-item="addItem">
     <template v-slot:default="slotProps">
        <Bookmark v-bind="slotProps.individualBookmark"></Bookmark>
    </template>
      </TreeView>

【讨论】:

谢谢,这最终将子文件夹呈现为文件夹。你知道我如何在子文件夹中呈现书签吗?子文件夹不显示它包含的书签。 不客气,请检查我编辑的答案,我使用模板元素呈现插槽内容 这似乎不起作用,这是我正在谈论的picture。子文件夹看起来已展开,但未显示其中包含的书签。此外,由于某种原因,当我单击子文件夹上的 [-] 按钮时,它会收回整个树,而它只应该收回子文件夹中的书签。 您能否提供一些codepen代码框示例以便对其进行调试 Here 是我的全部代码。再次感谢您的帮助。

以上是关于如何在 Vue.js 插槽中使用条件渲染?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Vue.js 的嵌套 v-for 循环中使用 v-html 有条件地渲染原始 HTML?

Vue.js 插槽 - 如何在计算属性中检索插槽内容

Vue.js 如何在插槽上使用属性

如何在 Vue.js 插槽范围内传递方法

Vue.js 3 - 将组件插入插槽

如何正确使用 vue js Web 组件内部的插槽并应用样式