1

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

Augustin Riedinger
  • 20,909
  • 29
  • 133
  • 206

2 Answers2

4

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.

Augustin Riedinger
  • 20,909
  • 29
  • 133
  • 206
1

You problem come from the fact that the $emit emit the function to the parent div, which is your case is the App.vue and not the Dialog.vue.


To solve your problem, you can add a ref to your Dialog and trigger the close function from the div

<div class="text-center my-12">
   <Dialog persistent ref="myDiv">
      <template v-slot:activator="{ on, attrs }">
         <v-btn color="primary" v-bind="attrs" v-on="on"> Click me </v-btn>
      </template>
      <DialogContent @close="onCloseFromParent" />
   </Dialog>
</div>

...

methods: {
    onCloseFromParent() {
      this.$refs.myDiv.onClose();
    },
},
RenaudC5
  • 3,553
  • 1
  • 11
  • 29
  • Thanks for your reply. My idea was to isolate the `` logic from the `` so that the latter doesn't need to know about the former, besides this `onClose` method. I'm loosing this benefit with your solution and there is not so much reason to separate them, as `` has to know about the ref. Ain't there any other solution? I reckon in React this would be trivial, which is why I feel like it should be doable. – Augustin Riedinger Apr 21 '22 at 09:09