q-select



  • Hi friends

    in the q-select component in the options, I assign it the model array, but it doesn’t show it

    my structure in the model is:

    options: {
    companies: [],
    employess: []
    }

    q-select.jpg



  • I think it only accepts a defined structure like this
      {
               label: ‘Uno’,
               value: ‘1’,
               description: ‘uno’,
               category: ‘1’
             }.



  • @eloy-silva Have a look to option-value and option-label props of q-select

    <q-select
      ...
      :options="options.companies"
      option-value="USR_ERP_COMPANY_ID"
      option-label="USR_ERP_NAME"
      ...
    />
    

    These props can also be a function if you need more control.



  • @tof06 Thanks



  • @tof06 I tried it and it works … but now the filter function gives error in toLowerCase

    TypeError: Cannot read property ‘toLowerCase’ of undefined"



  • @eloy-silva Pretty hard to guess why without viewing the code ! 🙂



  • @tof06 Hi,

    Hello, thanks for answering

    I am new to this framework (vuejs, quasar), and what I want to do is a reusable component, so as not to be repeating the filter functions, I just want to send the json object and that when filtering or selecting the item, it is reflected in my UI and in the model

    excuse my spelling, I don’t speak english

    <template>
      <q-select
        square
        dense
        outlined
        use-input
        input-debounce="0"
        v-model="valuemember"
        :option-value="optionvalue"
        :option-label="optionlabel"
        :options="dataset"
        :placeholder="placeholder"
        @filter="filterFn"
        
      >
      </q-select>
    </template>
    
    <script>
    export default {
      name: "optimusSelect",
      props: {
        dataSource: {
          type: Array,
          required: true,
          twoWay: true
        },
        optionvalue: {
          type: String,
          required: true
        },
        optionlabel: {
          type: String,
          required: true
        },
        placeholder: {
          type: String,
          required: false
        }
        // displaymember: {
        //   type: String,
        //   required: true,
        //   twoWay: true
        // },
        // valuemember: {
        //   type: String,
        //   required: true,
        //   twoWay: true
        // }
      },
    
      computed: {
        dataset: {
          get() {
            return this.dataSource;
          },
          set(value) {
            this.dataSource = value;
          }
        }
        // datavalue:{
        //     get(){
        //         return this.valuemember;
        //     },
        //     set(value){
        //         this.valuemember = value;
        //     }
        // }
      },
      data() {
        return {
          valuemember: null
        };
      },
      mounted() {},
      methods: {
        filterFn(val, update) {
          
    
              var that = this;
            if (val === "") {
              update(() => {
                //this.dataSource = objectOptions
                // with Quasar v1.7.4+
                // here you have access to "ref" which
                // is the Vue reference of the QSelect
              });
              return;
            }
    
            update(() => {
              const needle = val.toLowerCase();
              let data = that.dataSource;
              that.dataset   = data.filter(
                v => v.value.toLowerCase().indexOf(needle) > -1
              );
            });
        }
      }
    };
    </script>
    
    <style></style>
    
    

    in my page

    <template>
      <q-page padding class="page-normal">
        <q-form>
          <q-toolbar>
            <q-toolbar-title>Nuevo usuario</q-toolbar-title>
          </q-toolbar>
          <separatorH caption="Datos generales"></separatorH>
    
          <div class="row q-pa-md">
            <div class="f-v-label col-2">Compañia:</div>
            <div class="f-v-input col-2">
              <optimusSelect
                v-model="oEntity.USR_ERP_COMPANY_ID"
                :dataSource="options.companies"
                optionvalue="USR_ERP_COMPANY_ID"
                optionlabel="USR_ERP_NAME"
               
              />
            </div>
          </div>
    
          <div class="row q-pa-md">
            <div class="f-v-label col-2">Empleado:</div>
            <div class="f-v-input col-2">
              <!-- <lookup :dataSource="cities" :selection.sync="datavalue" placeholder="Buscar empleado"></lookup> -->
            </div>
          </div>
    
          <div class="row q-pa-md">
            <!--Label-->
            <div class="f-v-label col-2">Nombre de usuario:</div>
            <!--Input-->
            <div class="f-v-input col-2">
              <q-input
                square
                outlined
                autofocus
                dense
                v-model="datav"
                placeholder="Nombre de usuario:"
              >
              </q-input>
            </div>
          </div>
        </q-form>
      </q-page>
    </template>
    
    <script>
    import { mapState, mapActions } from "vuex";
    import separatorH from "components/separatorH";
    import lookup from "components/lookup";
    import optimusSelect from "components/optimus-select";
    
    export default {
      name: "user",
      data() {
        return {
          options: {
            companies: [
              {
              USR_ERP_COMPANY_ID: 100,
              USR_ERP_NAME: 'ACNE',
             
            },
            {
              USR_ERP_COMPANY_ID: 200,
              USR_ERP_NAME: 'SOCONSA',
            },
            
            ]
          },
          datav:null,
          oEntity: {
            USR_ERP_COMPANY_ID: '',
            USR_ERP_NAME: ""
          }
        };
      },
      components: {
        separatorH,
        lookup,
        optimusSelect
      },
      computed: {
        ...mapState("store", ["userDetails", "currentPage"])
        
      },
      methods: {
        ...mapActions("store", ["load_page"])
      },
      mounted() {
        // this.load_page({
        //   title: "Usuario",
        //   IsActive: true
        // });
      },
      destroyed() {
        // this.load_page({
        //   title: "",
        //   IsActive: false
        // });
      }
    };
    </script>
    
    


  • @tof06

    toLocaleLowerCase solutions !!!

    but in the filter function I have an error: 🙁

    [Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop’s value. Prop being mutated: “dataSource”

    try to fix it with computed:

    <template>
      <q-select
        square
        dense
        outlined
        v-model="valuemember"
        :option-value="optionvalue"
        :option-label="optionlabel"
        :options="dataSource"
        :placeholder="placeholder"
        use-input
        hide-selected
        fill-input
        input-debounce="0"
        @filter="filterFn"
        @input-value="setModel"
        @input="onChange"
        emit-value
        map-options
      >
        <template v-slot:no-option>
          <q-item>
            <q-item-section class="text-grey">
              No results
            </q-item-section>
          </q-item>
        </template>
      </q-select>
    </template>
    
    <script>
    export default {
      watch: {},
    
      computed: {
          dataset:{
              get(){
                return this.dataSource;
              },
              set(value){
                    this.dataSource = value;
                     this.$emit("dataset", value);
              }
          }
      },
      data() {
        return {
          valuemember: null,
          
        };
      },
      mounted(){
          
      },
      props: {
        dataSource: {
          type: Array,
          required: true,
          twoWay: true,
        },
        optionvalue: {
          type: String,
          required: true,
        },
        optionlabel: {
          type: String,
          required: true,
        },
        placeholder: {
          type: String,
          required: false,
        },
      },
      methods: {
        filterFn(val, update, abort) {
             if (val === '') {
            update(() => {
              //this.dataSource = this.dataset
    
              // with Quasar v1.7.4+
              // here you have access to "ref" which
              // is the Vue reference of the QSelect
            })
            return
          }
    
          update(() => {
            
            const needle = val.toLocaleLowerCase();
            this.dataSource = this.dataset.filter(
              (v) => v[this.optionlabel].toLocaleLowerCase().indexOf(needle) > -1
            );
    
             this.$emit("dataset", this.dataset);
          });
        },
    
        setModel(val) {
          this.model = val;
        },
        onChange(value) {
          //refresh and update model
          
          this.$emit("input", value);
        },
        getData() {
          return this.valuemember;
        },
        setData(value) {
          this.valuemember = value;
          this.$emit("input", value);
        },
      },
    };
    </script>
    


  • @eloy-silva As stated in VueJS Documentation, props are only one-way binding from parent to child, so, you can’t update them from child.

    Depending on the behavior you want, a simple data property will do the trick :

    export default {
      props: {
       dataSource: { type: Array, required: true },
       // ... 
      }, 
      data() {
        return {
          // ...
          _dataSource: this.dataSource
        }
      }
    

    Then, use _dataSource in your template instead of dataSource

    If you absolutly need dataSource to be sync in parent component, then, you’ll need to implement an event, and listen to this event on parent



  • @tof06 it worked perfectly.

    😊

    Thanks !!!



  • @tof06
    the problem with the props, it was overcome
    but according to what you indicate and following the example in the documentation, the filter function should look like this:
    and not work

    filterFn(val, update, abort) {
         
         
          update(() => {
            const needle = val.toLocaleLowerCase();
            
           this._dataSource =  this.dataSource.filter(
              (v) => v[this.optionlabel].toLocaleLowerCase().indexOf(needle) > -1
            );
    
          
            
          });
        },
    

    now in the documentation everything works fine, there it uses a constant of a string array, but when changing to a json object, things get very complicated

    q-select-filter.jpg



  • @tof06
    Follow your advice, check the documentation of props in VueJS and it is already solved
    I post it here if it serves someone or see how it can be improved.

    in the component:

    <!-- select autocomplete -->
    <template>
      <q-select
        v-model="valuemember"
        use-input
        hide-selected
        fill-input
        input-debounce="0"
        :option-value="optionvalue"
        :option-label="optionlabel"
        :options="dataSource"
        :placeholder="placeholder"
        @filter="filterFn"
        @input-value="setModel"
        square
        dense
        outlined
        @input="onChange"
        options-dense
        emit-value
        map-options
      >
        <template v-slot:no-option>
          <q-item>
            <q-item-section class="text-grey">
              No hay resultados
            </q-item-section>
          </q-item>
        </template>
      </q-select>
    </template>
    
    <script>
    let filtered;
    export default {
      
      computed: {
        // _dataSource: {
        //   get() {
        //     return this.dataSource;
        //   },
        //   set(value) {
        //     //console.log('set');
        //     this.$props.dataSource = value
        //     //this.$emit("dataSource", value);
        //   },
        // },
      },
      data() {
        return {
          valuemember: 100,
           _dataSource : this.dataSource
        };
      },
      watch: {},
      created() {},
      mounted() {},
      props: {
        dataSource: {
          type: Array,
          required: true,
        },
        optionvalue: {
          type: String,
          required: true,
        },
        optionlabel: {
          type: String,
          required: true,
        },
        placeholder: {
          type: String,
          required: false,
        },
      },
      methods: {
        
        filterFn(val, update, abort) {
         filtered = this._dataSource
          update(() => {
            const needle = val.toLocaleLowerCase();
    
            //
            // this.dataSource = this.$data._dataSource.filter(
            //   (v) => v[this.optionlabel].toLocaleLowerCase().indexOf(needle) > -1
            // );
    
            this._dataSource  = this.$data._dataSource.filter(
              (v) => v[this.optionlabel].toLocaleLowerCase().indexOf(needle) > -1
            );       
          });
          this.$emit('update-datasource', this._dataSource)
        },
    
        setModel(val) {
          this.valuemember = val;
        },
        onChange(value) {
          //refresh and update model
          this.$emit("input", value);
         
        },
        getData() {
          return this.valuemember;
        },
        setData(value) {
          this.valuemember = value;
          this.$emit("input", value);
        },
      },
    };
    </script>
    
    

    in the page:

     <OptimusSelect2 ref="sel2"
                v-model="USR_ERP_COMPANY_ID"
                :dataSource="options.companies"
                optionvalue="USR_ERP_COMPANY_ID"  
                optionlabel="USR_ERP_NAME"
                @update-datasource="UpdateCompany"
              />
    
     data() {
        return {
          options: {
            companies: [
              {
              USR_ERP_COMPANY_ID: 100,
              USR_ERP_NAME: 'ACME',
             
            },
            {
              USR_ERP_COMPANY_ID: 200,
              USR_ERP_NAME: 'SOCONSA',
            },
            
            ]
          },
    
    UpdateCompany(data){
         //Update
         this.options.companies = data;
        }
    

    your comments helped me research and learn more
    thank you so much

    Eloy


Log in to reply