Adding custom functionality to Quasar's components



  • First of all, I definitely do not consider myself to be an expert and there are tutorials which describe this process in more detail (for example: https://medium.com/quasar-framework/component-building-with-quasar-fc101b6730ae). However, sometimes you simply want to know how to do something without knowing all the ins and outs.

    Anyway, I noticed that since Quasar v1 beta was released, a lot of questions or remarks come down to “this was much easier in v0.17”. I also experienced this myself and as annoying as it is that you can’t simply do something, it is the only way to create a proper and maintainable framework. Let’s hope that in the future Quasar will get improved to a point where you can perform everything without programming anything, but until that time arrives we will have to add the functionality ourselves.

    So, the great thing about Quasar v1 is that they seemed to have considered every scenario when developing the base components, so by extending the base you can add your own functionality.

    For example, there is an Input and a Date component, and as explained in the documentation you can combine the two to get a date input (https://v1.quasar-framework.org/vue-components/date#Example--With-QInput). Now, I wanted to hide the popup on selection of a date: https://codepen.io/stefanvh/pen/XGJVMv

    This works fine as of itself, but imagine you would need 10 date inputs. That requires 90 lines of code, and in the case you would want to change something in the future, changing the same thing 10 times. So, what do we do? We create a reusable component:

    quasar new component QDateInput
    If we would copy the code from the codepen into QDateInput.vue, we can import QDateInput.vue as a component in any page in Quasar:

    import qDateInput from 'components/QDateInput.vue'
    
    export default {
      name: 'PageDates',
      components: {
        qDateInput
      }
    }
    

    In the template we can then use it with <q-date-input>:

    <template>
        <q-date-input />
    </template>
    

    However, QInput and QDate have a lot of properties which are and cannot be used in this way. To be able to set the underlying properties of QDate and QInput we will have to add the properties to our QDateInput component:

    <template>
      <q-input
        :value="formattedDate"
        v-bind="{ error, errorMessage, label, stackLabel, hint, hideHint, color, dark, filled, outlined, borderless, standout, bottomSlots, rounded, square, dense, readOnly }"
      >
      <template v-slot:append>
        <q-icon name="event" class="cursor-pointer">
          <q-popup-proxy ref="proxy">
            <q-date
              v-model="localValue"
              v-bind="{ landscape, color, textColor, dark, readonly, disable, firstDayOfWeek, todayBtn, minimal, options, events, eventColor }"
              @input="$refs.proxy.hide()"
            />
          </q-popup-proxy>
        </q-icon>
    ` </template>
    </q-input>
    </template>
    
    <script>
    import { date as dateUtil } from 'quasar'
    
    export default {
      name: 'QDateInput',
      props: {
        value: {
          type: String,
          default: ''
        },
        error: Boolean,
        errorMessage: String,
        label: String,
        stackLabel: String,
        hint: String,
        hideHint: Boolean,
        filled: Boolean,
        outlined: Boolean,
        borderless: Boolean,
        standout: Boolean,
        bottomSlots: Boolean,
        rounded: Boolean,
        square: Boolean,
        readOnly: Boolean,
        dense: Boolean,
        landscape: Boolean,
        color: String,
        textColor: String,
        dark: Boolean,
        readonly: Boolean,
        disable: Boolean,
        firstDayOfWeek: [String, Number],
        todayBtn: Boolean,
        minimal: Boolean,
        options: [Array, Function],
        events: [Array, Function],
        eventColor: [String, Function]
      },
      computed: {
        localValue: {
          get () { return this.value },
          set (localValue) { this.$emit('input', localValue) }
        },
        formattedDate () {
          return dateUtil.formatDate(this.value, 'DD/MM/YYYY')
        }
      }
    }
    </script>
    
    

    By defining the value prop, you are able to use v-model on the component. This will give a two-way binding with the data you use as v-model. Note that in order to set the date, the data is also coupled with v-model inside the component. Directly using value as v-model for QDate will result in a “Avoid mutating a prop directly” error, so instead we will have to use a local computed value which takes the value of value and emits an input event on change.
    Now we can use the component as followss:

    <q-date-input
      filled
      hint="Hint"
      bottom-slots
      first-day-of-week="1"
      :events="['2018/11/05', '2018/11/06', '2018/11/09', '2018/11/23']"
      ...
     />
    

    So you can use any of the props of either QInput and QDate and it will change them inside the component accordingly, and we added our own custom functionality. Hiding the popup on select is a really simple example, but it proves the point.

    “Wait, will I have to do this everytime I need a function that isn’t covered by Quasar out of the box?”
    Well, yes, or you let someone else do it. And that is what I think the app extensions will be for.



  • On your v-binds, you could just create the props object in the script. That will make your template code look cleaner/ simpler. 😄

    Nice write up! 👍

    Scott


Log in to reply