Vue: Support ref attribute in slots

Created on 17 Feb 2018  路  6Comments  路  Source: vuejs/vue

What problem does this feature solve?

When doing a composition of component like this:

Child component with slot

<template>
    <div>
        child
        <slot name="slot" ref="slot"/>
    </div>
</template>
<script>
    export default {
        name:'child'
    }
</script>

Parent component

<template>
    <div>
        parent
        <child>
            <custom-component slot="slot"/>
        </child>
    </div>
</template>
<script>
    import Child from './child.vue';
    import CustomComponent from 'custom-component';
    export default {
        name:'parent',
        components:{ Child, CustomComponent }
    }
</script>

it would be great to access custom-component (component not element ) using this.$refs.slot .
Currently its done using this statement this.$slots.slot[0].componentInstance , this.$refs.slot is undefined.

Use case is calling child slot component methods.

Thanks

What does the proposed API look like?

Accessing slot component using
this.$refs.slot

feature request

Most helpful comment

My usecase for supporting refs in a slot is getting the slotted component element reference for animation purposes. I see how calling the slotted methods is odd architecture, but it would be useful to get the element reference instead. Do you suggest I pass the element reference as a prop to the component with the slot instead or does this warrant further discussion?

All 6 comments

The usage does not sound like a good idea because:

  1. Slots are by definition indeterministic. It may be a single component, but may also be multiple components, a single element, or multiple elements. Ref's behavior will be really ambiguous here.

  2. Directly calling methods on a ref component is code smell. Use events or a state management pattern.

Agreed

My usecase for supporting refs in a slot is getting the slotted component element reference for animation purposes. I see how calling the slotted methods is odd architecture, but it would be useful to get the element reference instead. Do you suggest I pass the element reference as a prop to the component with the slot instead or does this warrant further discussion?

I'm trying to figure out the height of all elements in a slot, having a ref on the slot would be good, ot at least having $slots have some future proof way of knowing the children in it to measure (because we aren't supposed to use elm anymore).

Needed to do the same to scroll the slot into view when a property changes, managed with this.$slots.default[0].elm

Using elm is usually a bad practice but in this particular case, it's the only way

Depending on the syntax you use you may need to store the slot when mounting the component
https://github.com/vuejs/vue/issues/9580#issuecomment-467718522

I have a similar situation where I have a Component TransactionsPage which is using another component called Layout, Layout offers multiple slots; header and body.. and the css for handling scrollbar/overflow and height for the body slot is set in Layout

From the TransactionsPage I鈥檓 rendering two list groups within the body slot of the Layout component, I want to register a scroll event to load more data on scroll. How can I query the container Layout鈥檚 body template / slot from within TransactionsPage? setting a ref on the <template slot="body"> tag doesn't work

hardcoding the class name that is set in LayoutComponent with document.querySelector works, but this is obviously not any better because then the consuming component depends on the other's implementation and specific class name, what is the right way to do this?

<template>
  <layout>
      <template slot="header">
        <!-- some header -->
      </template>
      <template  slot="body">
         <transaction-group>
         <transaction-group>
      </template>
    </layout>
</template>

<script>
  mounted() {
    this.containerElement = document.querySelector('.layout__body');
    this.containerElement.addEventListener('scroll', this.throttledScroll, {
      passive: true,
    });
  },
  methods: {
    onScroll() {
      const bottomOfWindow = document.documentElement.scrollTop + window.innerHeight > document.documentElement.offsetHeight - 5;
      if (bottomOfWindow && !this.isBusy && !this.hasReachedTheEnd) {
        this.fetchMore();
      }
    }
</script>
Was this page helpful?
0 / 5 - 0 ratings