5
votes

I wrote a component (Component B) that accepts list of custom components via slot like this

// Component A
<div class="component-a">
  ...
  <component-b>
    <component-x></component-x>
    <component-y></component-y>
  </component-b>
  ...
</div>

and I want to wrap component x and y in other component, such as li tag.

// Output
...
<ul>
  <li><component-x></component-x></li>
  <li><component-y></component-y></li>
</ul>
...

I tried with

// Component B
<div class="component-b">
  <ul>
    <li v-for="item in $slots.default">
      <component :is="item"></component>
    </li>
  </ul>
</div>

It doesn't work. The item is VNode object and it can't render with component tag. Is there any solution to solve this problem?

Edit: My wrapping component is not li tag, it's a custom component with specified props that I set it in component B. If I wrap them from component A, I need to write the custom component and its props repeatedly.

Edit2: Render function maybe solve this problem, but I'm looking for solution with html template (single file component).

2
Maybe this works <component :is="item.name"></component> , just an idea. Because :is expects a compoent-name not an object reference.Reiner

2 Answers

5
votes

I guess it is impossible to make that without the adult version (render function).

PS: for a more detailed component without mess with render function I suggest insert another component for handle others functionalities, eg:

createElement(componentForMakeCoolThings,{props...})    

PS2: you can use the solution below in a single file component with a simple adaptation:

<script>
  export default{
     render: function(createElement){
     }
  }
</script>

Vue.component('makeLi',{
  render:function(createElement){
        var list = []
        this.$slots.default.forEach((element,index) => {
            if(element.tag){
                list.push(createElement('li',{},[element]))
                
            }
        });

        return createElement('ul',{},list)
    }

})

new Vue({el:'#app'});
<script src="https://vuejs.org/js/vue.min.js"></script>
<div id="app">
<make-li>
 <h1>test1</h1>
 <b>bold</b>
 <i>italic</i>
</make-li>


</div>

0
votes

This works?

<div class="component-a">
  <component-b>
    <component-x></component-x>
    <component-y></component-y>
  </component-b>
</div>

Then use

<div class="component-a">
  <component-b>
    <ul>
      <li><component-x></component-x></li>
      <li><component-y></component-y></li>
    </ul>
  </component-b>
</div>