Search code examples
javascriptvue.jsvuejs2componentsnested-loops

Vuejs nested dynamic component


Knowing that i'll maybe have even more children, is there another way to do nested component in VueJS?

I don't know in advance which component will be in the code so i use the dynamic ones but they will always have multiple children in them and not always the same amount

Here's the only solution i found Is there another way to this?

From my html:

<component :is="blocksElements[0].componentId">
    <component :is="blocksElements[0].child.componentId" v-if="blocksElements[0].hasChild">
        <component :is="blocksElements[0].child.child.componentId" v-if="blocksElements[0].child.hasChild" v-bind="blocksElements[0].child.child.props">
            <component :is="blocksElements[0].child.child.child.componentId" v-if="blocksElements[0].child.child.hasChild" v-bind="blocksElements[0].child.child.child.props"></component>
        </component>
    </component>
</component>

From my js:

blocksElements: [
    {
        componentId: 'row',
        hasChild: true,
        child: {
            componentId: 'column',
            hasChild: true,
            child: {
                componentId: 'block-image',
                hasChild: true,
                props: {
                    logo: true,
                    width: '120'
                },
                child: {
                    componentId: 'block-image',
                    hasChild: false,
                    props: {
                        logo: true,
                        width: '120'
                    }
                }
            }
        }
    }
]

Solution

  • You can use render functions to achieve this: https://v2.vuejs.org/v2/guide/render-function.html

    Here's an example (I wrote it very quickly so you can improve it further):

    <script>
    import Row from './Row.vue'
    import Column from './Column.vue'
    import BlockImage from './BlockImage.vue'
    
    export default {
      components: {
        Row,
        Column,
        BlockImage
      },
      data () {
        return {
          blocksElements: [
          {
            componentId: 'row',
            hasChild: true,
            child: {
              componentId: 'column',
              hasChild: true,
              child: {
                componentId: 'block-image',
                hasChild: true,
                props: {
                  logo: true,
                  width: '120'
                },
                child: {
                  componentId: 'block-image',
                  hasChild: false,
                  props: {
                    logo: false,
                    width: '130'
                  }
                }
              }
            }
          }
          ]
        }
      },
    
      render: function (h) {
        let els = []
        let result = null
        const buildElementBlocks = function (el) {
          els.unshift(el)
          if (el.hasChild) {
            return buildElementBlocks(el.child)
          }
          result = h(el.componentId, { props: el.props })
          els
            .splice(1)
            .forEach(element => {
              result = h(element.componentId, {
                props: element.props
              }, [result])
            })
        }
        buildElementBlocks(this.$data.blocksElements[0])
        return result
      }
    }
    </script>
    

    I hope this helps!