Search code examples
vue.jsvue-slot

Vue add event listener to slot


So I have this <Dialog /> component that extends vuetify's <v-dialog /> default.

In order to avoid having to pass a onClose method to the DialogContent component, I'd rather it $emit('close').

But I can't make my slot listen to this event. :(

Here's the code:

// Dialog.vue

<template>
  <v-dialog
    v-bind="$attrs"
    v-model="dialog"
  >
    <!-- forward other slots -->
    <template
      v-for="(_, slot) of otherSlots"
      v-slot:[slot]="scope"
    >
      <slot :name="slot" v-bind="scope" />
    </template>

    <template v-slot:default="{ on, attrs }">
      <slot name="default" v-on="on" v-bind="attrs" @close="onClose" />
    </template>
  </v-dialog>

</template>

<script>
  import {reject} from '@/utils/object';

  export default {
    inheritAttrs: false,
    computed: {
      otherSlots() {
        return reject(this.$scopedSlots, 'default');
      },
    },
    data() {
      return {
        dialog: false,
      }
    },
    methods: {
      onClose() {
        this.dialog = false;
      }
    },
  }
</script>

Usage:

      <Dialog persistent>
        <template v-slot:activator="{ on, attrs }">
          <v-btn
            v-bind="attrs"
            v-on="on"
          >
            My button
          </v-btn>
        </template>

        <DialogContent />
      </Dialog>

onClose is never called.

Any idea why?

Here's a sandbox to reproduce the issue: https://codesandbox.io/s/pass-event-listener-to-slot-ktemg9

Thanks


Solution

  • Sooo I finally succeeded to do what I wanted:

    Dialog.vue:

    <template>
      <v-dialog>
        <slot name="default" :onClose="onClose" />
      </v-dialog>
    </template>
    

    Usage:

    <template v-slot:default="{onClose}">
      <DialogContent @close="onClose" />
    </template>
    

    or to have a more vuetify-like syntax:

    Dialog.vue:

    <template>
      <v-dialog>
        <slot name="default" :on="{close: onClose}" />
      </v-dialog>
    </template>
    

    Usage:

    <template v-slot:default="{on}">
      <DialogContent v-on="on" />
    </template>
    

    I wish those props (or events) could be forwarded without having to pass them explicitly, but unfortunately this doesn't seem to be possible. :( Otherwise vuetify would do it for its activator slots.