Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vue.js dynamic layout renderless component layout with multiple slots

Tags:

vue.js

I am trying to build a dynamic layout for my application. I have two different layouts, one being DefaultLayout.vue:

<template>
  <div>
    <main>
      <slot/>
    </main>
  </div>
</template>

and a second one being LayoutWithFooter.vue, with two slots:

<template>
  <div>
    <main>
      <slot/>
    </main>
    <footer>
      <slot name="footer"/>
    </footer>
  </div>
</template>

My renderless component to handle the dynamic layout looks like this:

<script>
    import Vue from 'vue';
    import DefaultLayout from './DefaultLayout';
    import LayoutWithFooter from './LayoutWithFooter';

    export default {
        props: {
            name: {
                type: String,
                required: true
            }
        },
        created(){
            this.registerComponent("DefaultLayout", DefaultLayout);
            this.registerComponent("LayoutWithFooter", LayoutWithFooter);
            this.$parent.$emit('update:layout', this.name);
        },
        methods: {
            registerComponent(name, component) {
                if(!Vue.options.components[name]) {
                    Vue.component(name, component);
                }
            }
        },

        render() {
            return this.$slots.default[0];
        },
    }
</script>

All of this works fine for the DefaultLayout.vue but when I want to use the LayoutWithFooter.vue, it cannot handle the two slots inside it. Here's an example usage:

<template>
  <layout name="LayoutWithFooter">
    <div>
      <div>some content</div>
      <div slot="footer">content for the footer slot</div>
    </div> 
  </layout>
</template>

Problem now is, that the "content for the footer slot" does not get rendered inside of the footer slot of the LayoutWithFooter.vue.

like image 271
c16n Avatar asked Nov 06 '22 17:11

c16n


1 Answers

First of all I want you to pay an attention to defining slots level in your example. You provided this code:

<template>
  <layout name="LayoutWithFooter">
    <div>
      <div>some content</div>
      <div slot="footer">content for the footer slot</div>
    </div> 
  </layout>
</template>

But actually your div slot="footer" does not refer to footer slot of LayoutWithFooter.vue component. It because of anyway the very first child refers to default slot. And as a result it looks like:

"You want to set content for default slot and inside this default slot you tried to set content for footer slot" - but it's two different scopes.

The right options would look like on the next example:

<template>
  <layout name="LayoutWithFooter">
    <!-- default slot content -->
    <div>
      <div>some content</div>
    </div>

    <!-- footer slot content -->
    <div slot="footer">content for the footer slot</div>
  </layout>
</template>

I prepared some example based on code and structure you've provided. There you are able to switch layouts and check out how it works and use different components slot in one layout.

Check it here: https://codesandbox.io/s/sad-fog-zr39m

P.S. Maybe some point are not totally clear, please reply on my answer and I will try to explain more and/or provide you with more links and sources.

like image 168
Vadym Semenets Avatar answered Nov 15 '22 12:11

Vadym Semenets