How to extend a dialog



  • Hi,

    I want to extend a custom dialog component with a slot for its content.
    Therefore I have created this DialogWithSlot.vue:

    <template>
      <q-dialog v-model="visible">
        <q-card>
          <q-card-section>
            <div class="text-h6">Title</div>
          </q-card-section>
    
          <q-card-section class="q-pt-none">
            <slot></slot>
          </q-card-section>
    
          <q-card-actions align="right">
            <q-btn flat label="OK" color="primary" v-close-popup/>
          </q-card-actions>
        </q-card>
      </q-dialog>
    </template>
    
    <script>
    export default {
      name: 'DialogWithSlot',
    
      data() {
        return {
          visible: false
        }
      },
      methods: {
        showMessage() {
          this.visible = true
        }
      }
    }
    </script>
    

    Using this dialog can be done using

    <template>
      <q-page class="flex flex-center">
        <!--
          This dialog 'dialog-with-slot' approach works, but I want to move it to a seperate component
        -->
        <q-btn label="dialog with slot works" @click="openMessage('this is a message')"/>
        <dialog-with-slot ref="dlg">{{message}}</dialog-with-slot>
      </q-page>
    </template>
    
    <script>
    import DialogWithSlot from 'components/DialogWithSlot.vue'
    
    export default {
      name: 'PageInput',
    
      data() {
        return {
          message: false
        }
      },
      methods: {
        openMessage(msg) {
          this.message = msg
          this.$refs.dlg.showMessage()
        }
      },
      components: {
        DialogWithSlot
      }
    }
    </script>
    

    But now I want to have it in a seperated component file. So I have created this DialogWithSlotImpl.vue:

    <template>
      <dialog-with-slot>{{messageProp}}</dialog-with-slot>
    </template>
    
    <script>
    import DialogWithSlot from 'components/DialogWithSlot.vue'
    
    export default {
      name: 'DialogWithSlotImpl',
    
      extends: DialogWithSlot,
    
      props: ['messageProp'],
    
      data() {
        return {}
      },
      components: {
        DialogWithSlot
      }
    }
    </script>
    
    

    and use it like this:

    <template>
      <q-page class="flex flex-center">
        <!--
          This dialog 'dialog-with-slot-impl' approach don't work,
          How can I access the method (showMessage())
        -->
        <q-btn
          label="dialog with slot impl don't work"
          @click="openMessageImpl('this is yet another message')"
        />
        <dialog-with-slot-impl ref="implDlg" :messageProp="message"/>
      </q-page>
    </template>
    
    <script>
    import DialogWithSlotImpl from 'components/DialogWithSlotImpl.vue'
    
    export default {
      name: 'PageInput',
    
      data() {
        return {
          message: false
        }
      },
      methods: {
        openMessageImpl(msg) {
          this.message = msg
          this.$refs.implDlg.showMessage()
        }
      },
      components: {
        DialogWithSlotImpl
      }
    }
    </script>
    

    But this don’t work. The method showMessage() don’t get called.

    A complete codesandbox can be found here.
    https://codesandbox.io/s/extending-dialog-6mniu

    Somebody any idea’s



  • First, you don’t need to add the same component in both extends and components. In your case, extends is enough.
    Second, what you try to achieve is to programmatically set slot content.

    You can access the slots of your component through $slots.

    Here, your example with a working solution.
    https://codesandbox.io/s/extending-dialog-7960h



  • Hi @jraez ,

    Your solution does show the dialog. But its content (the message) is missing. So it does not work



  • Hi @jraez
    I found a solution of extending templates by using https://github.com/mrodal/vue-inheritance-loader
    This solution keeps the template syntax. The root dialog component now looks like:

    <template extendable>
        <q-dialog v-model="visible">
          <q-card>
            <q-card-section>
              <div class="text-h6">Title</div>
            </q-card-section>
    
            <q-card-section class="q-pt-none">
              <extension-point></extension-point>
            </q-card-section>
    
            <q-card-actions align="right">
              <q-btn flat label="OK" color="primary" v-close-popup />
            </q-card-actions>
          </q-card>
        </q-dialog>
    </template>
    
    <script>
    
    export default {
      name: 'DialogWithSlot',
    
      data () {
        return {
          visible: false
        }
      },
      methods: {
        showMessage () {
          this.visible = true
        }
      }
    }
    </script>
    
    

    and the child dialog looks like:

    <template extends="components/DialogWithSlot.vue">
       <extensions>
        <extension>
          {{messageProp}}
        </extension>
      </extensions>
    </template>
    
    <script>
    import DialogWithSlot from 'components/DialogWithSlot.vue'
    
    export default {
      name: 'DialogWithSlotImpl',
    
      extends: DialogWithSlot,
    
      props: ['messageProp'],
    
      data () {
        return {
        }
      }
    }
    </script>
    
    

    Looks very easy and straight forward to me.



  • @olaf my bad, was a typo but it works. I misspelled to prop. You can check, it’s working now.

    vue-inheritance is the same principle as inheritance in pug. You extend the child template 🙂


Log in to reply