q-select with select all option



  • Hi,

    I need a ‘select all’ option in the q-select checkbox type. So started to hack
    the legacy q-select component.

    The problem I have is when the ‘all’ must be checked (when all other options are selected) the last selected one still get checked.

    For readability, I omitted other types but checkbox.

    Here my custom q-select implementation :

    
    <template>
      <q-picker-textfield
        :disable="disable"
        :readonly="readonly"
        :label="label"
        :placeholder="placeholder"
        :static-label="staticLabel"
        :value="actualValue"
        @keydown.native.enter="open"
      >
        <q-popover ref="popover" :disable="disable || readonly" fit>
          <div class="q-select-popover list highlight">
            <label
              v-if="type === 'checkbox'"
              v-for="(checkbox, index) in options"
              :key="checkbox"
              class="item"
            >
              <div class="item-primary">
                <q-checkbox :value="optModel[index]" @click.prevent="onClick" @input="toggleValue(checkbox.value)"></q-checkbox>
              </div>
              <div class="item-content">
                <div v-html="checkbox.label"></div>
              </div>
            </label>
          </div>
        </q-popover>
      </q-picker-textfield>
    </template>
    
    <script>
    
    export default {
      props: {
        value: {
          required: true
        },
        options: {
          type: Array,
          required: true,
          validator (options) {
            return !options.some(opt =>
              typeof opt.label === 'undefined' || typeof opt.value === 'undefined'
            )
          }
        },
        type: {
          type: String,
          default: 'list',
          validator (value) {
            return ['radio', 'list', 'checkbox', 'toggle'].includes(value)
          }
        },
        label: String,
        placeholder: String,
        staticLabel: String,
        readonly: Boolean,
        disable: Boolean,
        all: Boolean,                      /// <= all boolean props
        delimiter: Boolean
      },
      computed: {
        model: {
          get () {
            if (this.multipleSelection && !Array.isArray(this.value)) {
              console.error('Select model needs to be an array when using multiple selection.')
            }
            return this.value
          },
          set (value) {
            this.$emit('input', value)
          }
        },
        optModel () {
          /* Used by multiple selection only */
          if (this.multipleSelection) {
            let options = this.options.map(opt => this.model.includes(opt.value))
            return options
          }
        },
        multipleSelection () {
          return ['checkbox', 'toggle'].includes(this.type)
        },
        actualValue () {
          if (!this.multipleSelection) {
            let option = this.options.find(option => option.value === this.model)
            return option ? option.label : ''
          }
    
          let options = this.options
            .filter(opt => this.model.includes(opt.value))
            .map(opt => opt.label)
    
          return !options.length ? '' : options.join(', ')
        }
      },
      created () {                                               // <= ADD ALL OPTION WHEN ALL IS  TRUE (French 'Tous' = 'All')
        if (this.all) {
          this.options.unshift({ label: 'Tous', value: 'all' })
        }
      },
      methods: {
        open (event) {
          if (!this.disable && !this.readonly) {
            this.$refs.popover.open(event)
          }
        },
        close () {
          this.$refs.popover.close()
        },
        toggleValue (value) {                          /// <= MY HACK
          let index = this.model.indexOf(value)
          // model copy to trigger the emit input
          let model = this.model.slice(0)
    
          if (index >= 0) {
            model.splice(index, 1)
            this.model = model
          }
          else {
            if (value === 'all') {
              this.model = ['all']
            }
            else {
              let indexAll = this.model.indexOf('all')
              if (indexAll >= 0) {
                model.splice(indexAll, 1)
                this.model = model
              }
    
              if (this.model.length + 1 === this.options.length - 1) {
                this.model = ['all']
              }
              else {
                model.push(value)
                this.model = model
              }
            }
          }
        },
        __setAndClose (val) {
          this.model = val
          this.close()
        }
      }
    }
    </script>
    

    My component usage :

                <my-select
                  all
                  class="column"
                  label="hotels"
                  type="checkbox"
                  v-model="hotels"
                  @input="onInput"
                  :options="selectOptions3"></my-select>
    

    My model :

    { 
          selectOptions3: [
            {
              label: 'Google',
              value: 'goog'
            }, {
              label: 'yahoo',
              value: 'ya'
            }, {
              label: 'Facebook',
              value: 'fb'
            }]
    }
    

    I will be really interested if someone has any idea about this or any advise on how to get a ‘select all’ feature on q-select

    thanks


  • Admin

    Will try to squeeze this feature in v0.14.


Log in to reply
 

Looks like your connection to Quasar Framework was lost, please wait while we try to reconnect.