Vue: Can't pass through named slots via functional component

Created on 20 Jun 2018  路  7Comments  路  Source: vuejs/vue

Version

2.5.16

Reproduction link

https://jsfiddle.net/qcabw2on/27/
https://jsfiddle.net/qcabw2on/21/

Steps to reproduce

Open above link

What is expected?

fallback(default)
content

What is actually happening?

fallback(abc)

Almost same as #7587

Document says we can delegate responsibility of slot system to child component by pass down context.children. But named slot does not work as expected.

  1. Is this an intentional bihaviour ?
  2. Is there any way to pass through named slots to child component (MyComponent)
    without changing code of MyComponent ?
    (The solution suggested in #7587 does not work in this case)

Most helpful comment

@helenezspeer it would be similar to the render function version:

<template functional>
  <child-component>
     <!-- this passes content as slot "abc" to the child-component -->
     <template slot="abc">
       <!-- this accepts the content for slot "abc" from your component -->
       <slot name="abc">
         <!-- this is the fallback content for the slot "abc" if none is provided -->
         <span>Fallback content</span>
       </slot>
     </template>
     <!-- this goes in child-component's default slot -->
     <p>Some text</p>
  </child-component>
</template>

For the new slot syntax, you would change <template slot="abc"> to <template v-slot:abc>

All 7 comments

I have the same problem

contextVm = Object.create(parent);

function FunctionalRenderContext (
  data,
  props,
  children,
  parent,
  Ctor
) {
  var options = Ctor.options;
  // ensure the createElement function in functional components
  // gets a unique context - this is necessary for correct named slot check
  var contextVm;
  if (hasOwn(parent, '_uid')) {
    contextVm = Object.create(parent);
    // $flow-disable-line
    contextVm._original = parent;
  } else {
    // the context vm passed in is a functional context as well.
    // in this case we want to make sure we are able to get a hold to the
    // real context instance.
    contextVm = parent;
    // $flow-disable-line
    parent = parent._original;
  }
  var isCompiled = isTrue(options._compiled);
  var needNormalization = !isCompiled;`

child.context === context || child.fnContext === context always false

function resolveSlots (
  children,
  context
) {
  var slots = {};
  if (!children) {
    return slots
  }
  for (var i = 0, l = children.length; i < l; i++) {
    var child = children[i];
    var data = child.data;
    // remove slot attribute if the node is resolved as a Vue slot node
    if (data && data.attrs && data.attrs.slot) {
      delete data.attrs.slot;
    }
    // named slots should only be respected if the vnode was rendered in the
    // same context.
    if ((child.context === context || child.fnContext === context) &&
      data && data.slot != null
    ) {
      var name = data.slot;
      var slot = (slots[name] || (slots[name] = []));
      if (child.tag === 'template') {
        slot.push.apply(slot, child.children || []);
      } else {
        slot.push(child);
      }
    } else {
      (slots.default || (slots.default = [])).push(child);
    }
  }

Sorry, reproduction link was wrong.

As I said in the other issue, you need to explicitly pass down the slot: https://jsfiddle.net/cedw34ur/

Thank you for your advice.

How do we pass down the named slots with a template-based functional component?

@helenezspeer it would be similar to the render function version:

<template functional>
  <child-component>
     <!-- this passes content as slot "abc" to the child-component -->
     <template slot="abc">
       <!-- this accepts the content for slot "abc" from your component -->
       <slot name="abc">
         <!-- this is the fallback content for the slot "abc" if none is provided -->
         <span>Fallback content</span>
       </slot>
     </template>
     <!-- this goes in child-component's default slot -->
     <p>Some text</p>
  </child-component>
</template>

For the new slot syntax, you would change <template slot="abc"> to <template v-slot:abc>

I finally had the opportunity to make this work, and it worked like a charm! Thank you @tmorehouse!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

yyx990803 picture yyx990803  路  48Comments

rpkilby picture rpkilby  路  50Comments

Akryum picture Akryum  路  34Comments

yyx990803 picture yyx990803  路  36Comments

RashadSaleh picture RashadSaleh  路  51Comments