Configure component prop defaults



  • @jraez that’s amazing. Setting the same property values( like dense outlined ect) for certain q-components is something you do over and over again in every quasar project.

    Then why is everybody so focused on a composition solution for this problem (composition has it’s uses but will ‘cripple’ the ‘extended’ component, explained in the posts above) and something simple as extending is never mentioned ( Quasar docs)? Are there some untold cave pits with extending vue/quasar components this way? Just really curious …



  • @ajenkins you’re welcome.

    @dobbel Well, it’s only my guess but it’s related to people’s knowledge of OOP, not really quasar or VueJS. After many years of programming, whatever the language, framework, or such, you’ll always land on coding fundamentals 🙂 Maybe there’s a downside using inheritance but I’d never face it (in a JS/VueJS/Quasar context).



  • Coz both have their uses, in wrapping you can fiddle with the template, which you cant using extend, unless you want to break it. In this case extend is appropriate since you are just changing the props.



  • For extending my own component template I use pug. I can create the template ‘template’ in parent with block and override what I need in children. The downside is to use 2 files one for Vue component, one for pug template, and include manually the pug into the component each time.



  • @jraez That sounds like a very flexible solution! Would you want to share an example of extending a quasar component with ‘extend’ combined with overriding a part of the parent template with pug?



  • In my case, I use it to create custom QDdialog and inject them through the Dialog plugin. The idea is to have a standard design with an icon + title, some action buttons, and the content.

    I changed the ProductDialog to something out of my own scope because it’ll be too complex for an example.

    The base dialog

    <script>
    export default {
      props: ['name'],
      data: () => ({
        config: {
          title: '',
          position: 'standard',
          translateTitle: true,
          cssClass: 'custom-dialog',
          icon: 'fas fa-exclamation-triangle'
        }
      }),
      methods: {
        // following method is REQUIRED
        // (don't change its name --> "show")
        show () {
          this.$refs.dialog.show()
        },
    
        // following method is REQUIRED
        // (don't change its name --> "hide")
        hide () {
          this.$refs.dialog.hide()
        },
    
        onDialogHide () {
          // required to be emitted
          // when QDialog emits "hide" event
          this.$emit('hide')
        },
    
        onOKClick () {
          // on OK, it is REQUIRED to
          // emit "ok" event (with optional payload)
          // before hiding the QDialog
          this.$emit('ok')
          // or with payload: this.$emit('ok', { ... })
    
          // then hiding dialog
          this.hide()
        },
    
        onCancelClick () {
          // we just need to hide dialog
          this.hide()
        }
      }
    }
    </script>
    <style>
    .custom-dialog {
      width: 600px;
    }
    </style>
    

    The dialog template:

    q-dialog(ref="dialog" @hide="onDialogHide" :persistent="true" :position="config.position")
      q-card(:class="config.cssClass")
        block wrapper
          block title
            q-card-section.row.items-center.text-h4
              q-icon(:name="config.icon").q-mr-md
              .text-capitalize {{ config.translateTitle ? $t(config.title) : config.title}}
              q-space
              q-icon(@click="onCancelClick" name="fas fa-times").cursor-pointer.q-ml-md
          block separator
            q-separator(inset)
          q-card-section(:horizontal="config.horizontal")
            block content
        q-card-actions.justify-end
          block actions
            q-btn(color="primary" flat :label="$t('application.actions.cancel')" @click="onCancelClick")
            q-btn(color="primary" :text-color="$theme.colors.primaryText" :label="$t('application.actions.save')" @click="onOKClick")
    

    The my dialog implement:

      <template lang="pug">
      extends ../../UI/Dialog/DialogBase
      block content
        q-card-section.col-4
          .text-body2 {{ $t('application.product.list') }}
          q-scroll-area(style="height: 700px;")
            q-list.q-mt-md
              q-item(v-for="product in products" :key="product.id" dense)
                q-item-section(side)
                  q-btn(flat round icon="fas fa-times" @click="remove(product)")
                q-item-section(side)
                  q-btn(flat round icon="fas fa-edit" @click="edit(product)")
        q-separator(vertical)
        q-card-section.col
          .text-body2.q-mb-sm  {{ $t('application.product.info') }}
          .row.q-gutter-none 
             q-editor(v-model="product.description")
      block actions
        q-btn(color="primary" flat :label="$t('application.actions.reset')" @click="reset")
        q-btn(color="primary" :text-color="$theme.colors.primaryText" :label="$t('application.actions.save')" @click="save")
    </template>
    <script>
    import DialogBase from '../../UI/Dialog/DialogBase'
    export default {
      name: 'ProductManagerDialog',
      extends: DialogBase,
      data: () => ({
        config: {
          title: 'application.product.manager.title',
          cssClass: 'project-product-manager',
          icon: 'fas fa-calendar-alt',
          horizontal: true
        },
        products: []
        product: null
      }),
      methods: {
        edit (product) {
          this.product = product
        },
        async remove (product) {
           // remove product from products and save to the backend
        }
      },
      async mounted () {
        this.products = await ProductService.list()
      }
    }
    </script>
    <style>
      .project-product-manager {
        min-width: 1100px;
      }
    </style>
    


  • @jraez Thanks for the sample code. Do you like to use Pug to code (Quasar templates) or is it necessary because your want to override templates?



  • @dobbel I use pug because it makes the template more readable to me (less “verbose” in fact), so I’ve started using it before the need to override templates. Then it helps me with overriding and composition, so I stick to it.



  • @jraez Tried extending QInput but I’m getting this error:
    Failed to mount component: template or render function not defined.

    Tried copy pasting your example but I get the same error

    MyInput.vue

    <script>
    import QInput from 'quasar'
    export default {
      name: 'MyInput',
      extends: QInput,
      props: {
        outlined: {
          type: Boolean,
          default: true
        },
        dense: {
          type: Boolean,
          default: true
        }
      }
     }
    </script>
    

    Page.vue

    <template>
      <div>
        <my-input></my-input>
      </div>
    </template>
    
    <script>
    import MyInput from 'components/MyInput'
    
    export default {
      components: { MyInput },
      name: 'PageIndex'
    }
    </script>
    
    
    

    Am I doing it wrong?



  • Nvm. I was just missing the curly brace 😁

    <script>
    import { QInput } from 'quasar'
    export default {
      name: 'MyInput',
      extends: QInput,
      props: {
        outlined: {
          type: Boolean,
          default: true
        },
        dense: {
          type: Boolean,
          default: true
        }
      }
     }
    </script>
    

Log in to reply