EOG
EOG

Reputation: 1747

Vuejs - pass slot to nested child

I've got a modal component that looks like this

//modal component
<template>
   <slot></slot>
   <slot name='buttons'></slot>
</template>

I want to use it as a wizard

//wizard component
<template>
   <modal>
      <step1-state v-if='step === 1'/>
      <step2-state v-if='step === 2'/>
   </modal>
</template>

Is there a way that step1,step2,etc consume buttons slot?

This does not seem to work

//step1-state component
<template>
   <div>
       <span>Does it work?</span>
       <div slot='buttons'>
          <button>yes</button>
          <button>no</button>
       </div>
   </div>
</template>

Upvotes: 3

Views: 3227

Answers (2)

EOG
EOG

Reputation: 1747

After some research I found that this is not possible by default. I was able to achieve this using custom render functions.

child-slot component - this one needs to be placed inside parent slot content, it enables you to format content of child component. You can add more than one child-slot to mix it with parent content. It needs to define ref attribute with distinct value.

use-slot component - this one is used to tell what content should be passed to parent as child-slot content. Content will be placed inside span element. It needs named and container attributes.

named - name of slot to place content in

container - ref value of child-slot

use-slot will automatically find first parent slot named by named attribute regardless of child depth

child-slot

<script>
    export default {
        render(createElement) {
            return createElement('span', {}, this.content);
        },
        data() {
            return {
                nodesToRender: {}
            }
        },
        computed: {
            content() {
                var result = [];
                for (var node in this.nodesToRender) {
                    result = result.concat(this.nodesToRender[node] || []);
                }
                return result;
            }
        }
    }
</script>

use-slot

<script>
    export default {
        abstract: true,
        props: {
            named: {
                type: String,
                required: true
            },
            container: {
                type: String,
                required: true
            }
        },
        render() {
            var parent = this.findParentOfSlot();
            var content = this.$slots.default;
            var self = this;
            this.$parent.$nextTick(function () {
                if (parent && parent.$refs && parent.$refs[self.container], parent.$refs[self.container].nodesToRender) {
                    Vue.set(parent.$refs[self.container].nodesToRender, self.$vnode.tag, content);
                }
            });
        },
        methods: {
            findParentOfSlot() {
                var parent = this.$parent;
                while (parent) {
                    if (parent.$slots && parent.$slots[this.named]) {
                        return parent.$parent;
                    }
                    parent = parent.$parent;
                }
                return parent;
            }
        }
    }
</script>

Upvotes: 1

Cristi Jora
Cristi Jora

Reputation: 1752

As far as I know, this is not possible currently and doesn't make too much sense because it becomes complicated and hard to read. Imagine looking at such nested slot code after 3 months of writing it. If both your step components require this buttons slot then write it as a sibling near them and send props whenever you have changes from this buttons slot

By the way, there seems to be a wizard component out there https://github.com/cristijora/vue-form-wizard , accidentally written by myself ))

Upvotes: 0

Related Questions